@Override
  public ModelNode getModelDescription(Locale locale) {
    ModelNode result = new ModelNode();

    final ResourceBundle bundle = descriptionResolver.getResourceBundle(locale);
    result.get(DESCRIPTION).set(descriptionResolver.getResourceDescription(locale, bundle));

    // Output min and max occurs if they are non-default values
    int minOccurs = registration.getMinOccurs();
    if (minOccurs > 0) {
      result.get(MIN_OCCURS).set(minOccurs);
    }
    int maxOccurs = registration.getMaxOccurs();
    PathAddress pa = registration.getPathAddress();
    if (pa == null || pa.size() == 0) {
      // Root node has no documented 'default'
      result.get(MAX_OCCURS).set(maxOccurs);
    } else {
      int defaultMax = pa.getLastElement().isWildcard() ? Integer.MAX_VALUE : 1;
      if (maxOccurs != defaultMax) {
        result.get(MAX_OCCURS).set(maxOccurs);
      }
    }

    Set<? extends Capability> capabilities = registration.getCapabilities();
    if (capabilities != null && !capabilities.isEmpty()) {
      for (Capability capability : capabilities) {
        ModelNode cap = result.get(ModelDescriptionConstants.CAPABILITIES).add();
        cap.get(NAME).set(capability.getName());
        cap.get(DYNAMIC).set(capability.isDynamicallyNamed());
      }
    }

    if (deprecationData != null) {
      ModelNode deprecated = addDeprecatedInfo(result);
      deprecated
          .get(ModelDescriptionConstants.REASON)
          .set(descriptionResolver.getResourceDeprecatedDescription(locale, bundle));
    }
    if (registration.isRuntimeOnly()) {
      result.get(ModelDescriptionConstants.STORAGE).set(ModelDescriptionConstants.RUNTIME_ONLY);
    }
    AccessConstraintDescriptionProviderUtil.addAccessConstraints(
        result, registration.getAccessConstraints(), locale);

    // Sort the attribute descriptions based on attribute group and then attribute name
    Set<String> attributeNames = registration.getAttributeNames(PathAddress.EMPTY_ADDRESS);

    Map<AttributeDefinition.NameAndGroup, ModelNode> sortedDescriptions = new TreeMap<>();
    for (String attr : attributeNames) {
      AttributeAccess attributeAccess =
          registration.getAttributeAccess(PathAddress.EMPTY_ADDRESS, attr);
      AttributeDefinition def = attributeAccess.getAttributeDefinition();
      if (def != null) {
        ModelNode attrDesc = new ModelNode();
        // def will add the description to attrDesc under "attributes" => { attr
        def.addResourceAttributeDescription(attrDesc, descriptionResolver, locale, bundle);
        sortedDescriptions.put(
            new AttributeDefinition.NameAndGroup(def), attrDesc.get(ATTRIBUTES, attr));
      } else {
        // Just store a placeholder
        sortedDescriptions.put(new AttributeDefinition.NameAndGroup(attr), new ModelNode());
      }
    }

    // Store the sorted descriptions into the overall result
    final ModelNode attributes = result.get(ATTRIBUTES).setEmptyObject();
    for (Map.Entry<AttributeDefinition.NameAndGroup, ModelNode> entry :
        sortedDescriptions.entrySet()) {
      attributes.get(entry.getKey().getName()).set(entry.getValue());
    }

    result.get(OPERATIONS); // placeholder

    result.get(NOTIFICATIONS); // placeholder

    final ModelNode children = result.get(CHILDREN).setEmptyObject();

    Set<PathElement> childAddresses = registration.getChildAddresses(PathAddress.EMPTY_ADDRESS);
    Set<String> childTypes = new HashSet<String>();
    for (PathElement childAddress : childAddresses) {
      String key = childAddress.getKey();
      if (childTypes.add(key)) {
        final ModelNode childNode = children.get(key);
        childNode
            .get(DESCRIPTION)
            .set(descriptionResolver.getChildTypeDescription(key, locale, bundle));
        childNode.get(MODEL_DESCRIPTION); // placeholder
      }
    }

    return result;
  }
  @Override
  public ModelNode getModelDescription(Locale locale) {
    ModelNode result = new ModelNode();

    final ResourceBundle bundle = descriptionResolver.getResourceBundle(locale);
    result.get(DESCRIPTION).set(descriptionResolver.getResourceDescription(locale, bundle));

    if (deprecationData != null) {
      ModelNode deprecated = addDeprecatedInfo(result);
      deprecated
          .get(ModelDescriptionConstants.REASON)
          .set(descriptionResolver.getResourceDeprecatedDescription(locale, bundle));
    }
    if (registration.isRuntimeOnly()) {
      result.get(ModelDescriptionConstants.STORAGE).set(ModelDescriptionConstants.RUNTIME_ONLY);
    }
    AccessConstraintDescriptionProviderUtil.addAccessConstraints(
        result, registration.getAccessConstraints(), locale);

    // Sort the attribute descriptions based on attribute group and then attribute name
    Set<String> attributeNames = registration.getAttributeNames(PathAddress.EMPTY_ADDRESS);

    Map<AttributeDefinition.NameAndGroup, ModelNode> sortedDescriptions = new TreeMap<>();
    for (String attr : attributeNames) {
      AttributeAccess attributeAccess =
          registration.getAttributeAccess(PathAddress.EMPTY_ADDRESS, attr);
      AttributeDefinition def = attributeAccess.getAttributeDefinition();
      if (def != null) {
        ModelNode attrDesc = new ModelNode();
        // def will add the description to attrDesc under "attributes" => { attr
        def.addResourceAttributeDescription(attrDesc, descriptionResolver, locale, bundle);
        sortedDescriptions.put(
            new AttributeDefinition.NameAndGroup(def), attrDesc.get(ATTRIBUTES, attr));
      } else {
        // Just store a placeholder
        sortedDescriptions.put(new AttributeDefinition.NameAndGroup(attr), new ModelNode());
      }
    }

    // Store the sorted descriptions into the overall result
    final ModelNode attributes = result.get(ATTRIBUTES).setEmptyObject();
    for (Map.Entry<AttributeDefinition.NameAndGroup, ModelNode> entry :
        sortedDescriptions.entrySet()) {
      attributes.get(entry.getKey().getName()).set(entry.getValue());
    }

    result.get(OPERATIONS); // placeholder

    result.get(NOTIFICATIONS); // placeholder

    final ModelNode children = result.get(CHILDREN).setEmptyObject();

    Set<PathElement> childAddresses = registration.getChildAddresses(PathAddress.EMPTY_ADDRESS);
    Set<String> childTypes = new HashSet<String>();
    for (PathElement childAddress : childAddresses) {
      String key = childAddress.getKey();
      if (childTypes.add(key)) {
        final ModelNode childNode = children.get(key);
        childNode
            .get(DESCRIPTION)
            .set(descriptionResolver.getChildTypeDescription(key, locale, bundle));
        childNode.get(MODEL_DESCRIPTION); // placeholder
      }
    }

    return result;
  }
  @Override
  public ModelNode getModelDescription(Locale locale) {
    ModelNode result = new ModelNode();

    final ResourceBundle bundle = descriptionResolver.getResourceBundle(locale);
    final ResourceBundle attributeBundle = attributeDescriptionResolver.getResourceBundle(locale);
    result.get(OPERATION_NAME).set(operationName);
    result
        .get(DESCRIPTION)
        .set(descriptionResolver.getOperationDescription(operationName, locale, bundle));

    result.get(REQUEST_PROPERTIES).setEmptyObject();

    if (parameters != null) {
      for (AttributeDefinition definition : parameters) {
        definition.addOperationParameterDescription(
            result, operationName, attributeDescriptionResolver, locale, attributeBundle);
      }
    }

    final ModelNode reply = result.get(REPLY_PROPERTIES).setEmptyObject();

    if (replyType != null && replyType != ModelType.UNDEFINED) {
      reply.get(TYPE).set(replyType);
      if (replyType == ModelType.LIST || replyType == ModelType.OBJECT) {
        if (replyValueType != null && replyValueType != ModelType.OBJECT) {
          reply.get(VALUE_TYPE).set(replyValueType);
        } else if (replyValueType != null) {
          reply
              .get(VALUE_TYPE)
              .set(getReplyValueTypeDescription(descriptionResolver, locale, bundle));
        }
      }
    }
    if (replyParameters != null && replyParameters.length > 0) {
      if (replyParameters.length == 1) {
        AttributeDefinition ad = replyParameters[0];
        ModelNode param = ad.getNoTextDescription(true);
        final String description =
            attributeDescriptionResolver.getOperationParameterDescription(
                operationName, ad.getName(), locale, attributeBundle);
        param.get(ModelDescriptionConstants.DESCRIPTION).set(description);
        reply.set(param);
        ModelNode deprecated = ad.addDeprecatedInfo(result);
        if (deprecated != null) {
          deprecated
              .get(ModelDescriptionConstants.REASON)
              .set(
                  attributeDescriptionResolver.getOperationParameterDeprecatedDescription(
                      operationName, ad.getName(), locale, attributeBundle));
        }
      } else {
        reply.get(TYPE).set(ModelType.OBJECT);
        for (AttributeDefinition ad : replyParameters) {
          final ModelNode param =
              ad.addOperationParameterDescription(
                  new ModelNode(), operationName, attributeDescriptionResolver, locale, bundle);
          reply.get(VALUE_TYPE, ad.getName()).set(param);
        }
      }
    }
    if (!reply.asList().isEmpty()) {
      final String replyDesc =
          descriptionResolver.getOperationReplyDescription(operationName, locale, bundle);
      if (replyDesc != null) {
        reply.get(DESCRIPTION).set(replyDesc);
      }
    }
    if (deprecationData != null) {
      ModelNode deprecated = result.get(ModelDescriptionConstants.DEPRECATED);
      deprecated.get(ModelDescriptionConstants.SINCE).set(deprecationData.getSince().toString());
      deprecated
          .get(ModelDescriptionConstants.REASON)
          .set(
              descriptionResolver.getOperationDeprecatedDescription(operationName, locale, bundle));
    }

    return result;
  }