static void buildTransformation(
      ModelVersion version, ResourceTransformationDescriptionBuilder builder) {

    if (InfinispanModel.VERSION_4_0_0.requiresTransformation(version)) {
      // Converts pool name to its JNDI name
      Converter converter =
          new Converter() {
            @Override
            public void convert(
                PathAddress address,
                String name,
                ModelNode value,
                ModelNode model,
                TransformationContext context) {
              if (value.isDefined()) {
                PathAddress rootAddress = address.subAddress(0, address.size() - 4);
                PathAddress subsystemAddress =
                    rootAddress.append(
                        PathElement.pathElement(
                            ModelDescriptionConstants.SUBSYSTEM, "datasources"));
                Resource subsystem = context.readResourceFromRoot(subsystemAddress);
                String poolName = value.asString();
                for (String type : Arrays.asList("data-source", "xa-data-source")) {
                  if (subsystem.hasChildren(type)) {
                    for (Resource.ResourceEntry entry : subsystem.getChildren(type)) {
                      if (entry.getName().equals(poolName)) {
                        value.set(entry.getModel().get("jndi-name"));
                        return;
                      }
                    }
                  }
                }
              }
            }
          };
      builder
          .getAttributeBuilder()
          .addRename(Attribute.DATA_SOURCE.getName(), DeprecatedAttribute.DATASOURCE.getName())
          .setValueConverter(
              new SimpleAttributeConverter(converter), Attribute.DATA_SOURCE.getDefinition());
    }

    if (InfinispanModel.VERSION_3_0_0.requiresTransformation(version)) {
      builder
          .addOperationTransformationOverride(ModelDescriptionConstants.ADD)
          .setCustomOperationTransformer(
              new SimpleOperationTransformer(new LegacyPropertyAddOperationTransformer()))
          .inheritResourceAttributeDefinitions();
    }

    if (InfinispanModel.VERSION_2_0_0.requiresTransformation(version)) {
      builder
          .getAttributeBuilder()
          .setDiscard(DiscardAttributeChecker.UNDEFINED, Attribute.DIALECT.getDefinition())
          .addRejectCheck(RejectAttributeChecker.DEFINED, Attribute.DIALECT.getDefinition())
          .end();
    }

    StoreResourceDefinition.buildTransformation(version, builder);
  }
  private static ModelFixer createModelFixer(ModelVersion version) {
    return (ModelNode model) -> {
      if (InfinispanModel.VERSION_4_1_0.requiresTransformation(version)) {
        final ModelNode maximal = model.get("cache-container", "maximal");
        maximal
            .asPropertyList()
            .stream()
            .filter(
                caches ->
                    caches.getName().equals("distributed-cache")
                        || caches.getName().equals("replicated-cache"))
            .forEach(
                p -> {
                  ModelNode caches = maximal.get(p.getName());
                  final List<Property> cachesModel = caches.asPropertyList();
                  for (Property cacheName : cachesModel) {
                    final ModelNode cache = caches.get(cacheName.getName());
                    if (cache.hasDefined("component")) {
                      cache.get("component", "backups").set(new ModelNode());
                    }
                  }
                });
      }

      if (InfinispanModel.VERSION_4_0_0.requiresTransformation(version)) {
        // Fix the legacy model to expect new default values applied in
        // StateTransferResourceDefinition#buildTransformation
        Arrays.asList("cache-with-string-keyed-store", "cache-with-binary-keyed-store")
            .forEach(
                cacheName -> {
                  ModelNode cache =
                      model.get("cache-container", "maximal", "replicated-cache", cacheName);
                  assertFalse(
                      cache.hasDefined(
                          StateTransferResourceDefinition.LEGACY_PATH.getKeyValuePair()));
                  ModelNode stateTransfer =
                      cache.get(StateTransferResourceDefinition.LEGACY_PATH.getKeyValuePair());
                  stateTransfer
                      .get(
                          StateTransferResourceDefinition.Attribute.CHUNK_SIZE
                              .getDefinition()
                              .getName())
                      .set(
                          StateTransferResourceDefinition.Attribute.CHUNK_SIZE
                              .getDefinition()
                              .getDefaultValue());
                  stateTransfer
                      .get(
                          StateTransferResourceDefinition.Attribute.TIMEOUT
                              .getDefinition()
                              .getName())
                      .set(
                          StateTransferResourceDefinition.Attribute.TIMEOUT
                              .getDefinition()
                              .getDefaultValue());
                });
      }
      return model;
    };
  }
  static void buildTransformation(
      ModelVersion version, ResourceTransformationDescriptionBuilder builder) {

    StateTransferResourceDefinition.buildTransformation(version, builder);

    if (InfinispanModel.VERSION_4_0_0.requiresTransformation(version)) {
      builder.addChildResource(
          PartitionHandlingResourceDefinition.PATH, new RequiredChildResourceDiscardPolicy());
    } else {
      PartitionHandlingResourceDefinition.buildTransformation(version, builder);
    }

    if (InfinispanModel.VERSION_2_0_0.requiresTransformation(version)) {
      final ResourceTransformationDescriptionBuilder backupsBuilder =
          builder.addChildResource(
              BackupsResourceDefinition.PATH, new RequiredChildResourceDiscardPolicy());
      backupsBuilder.rejectChildResource(BackupResourceDefinition.WILDCARD_PATH);

      builder.addChildResource(
          BackupForResourceDefinition.PATH, new RequiredChildResourceDiscardPolicy());
    } else {
      BackupsResourceDefinition.buildTransformation(version, builder);
      BackupForResourceDefinition.buildTransformation(version, builder);
    }

    ClusteredCacheResourceDefinition.buildTransformation(version, builder);
  }
  static void buildTransformation(
      ModelVersion version, ResourceTransformationDescriptionBuilder parent) {
    ResourceTransformationDescriptionBuilder builder =
        InfinispanModel.VERSION_4_0_0.requiresTransformation(version)
            ? parent.addChildRedirection(PATH, LEGACY_PATH)
            : parent.addChildResource(PATH);

    if (InfinispanModel.VERSION_4_0_0.requiresTransformation(version)) {
      builder
          .getAttributeBuilder()
          .setValueConverter(
              new AttributeConverter.DefaultAttributeConverter() {
                @Override
                protected void convertAttribute(
                    PathAddress address,
                    String attributeName,
                    ModelNode attributeValue,
                    TransformationContext context) {
                  if (attributeValue.isDefined()) {
                    List<ModelNode> remoteServers = attributeValue.clone().asList();
                    ModelNode legacyListObject = new ModelNode();
                    for (ModelNode server : remoteServers) {
                      ModelNode legacyListItem = new ModelNode();
                      legacyListItem.get("outbound-socket-binding").set(server);
                      legacyListObject.add(legacyListItem);
                    }
                    attributeValue.set(legacyListObject);
                  }
                }
              },
              Attribute.SOCKET_BINDINGS.getDefinition());
    }

    if (InfinispanModel.VERSION_3_0_0.requiresTransformation(version)) {
      builder
          .addOperationTransformationOverride(ModelDescriptionConstants.ADD)
          .setCustomOperationTransformer(
              new SimpleOperationTransformer(new LegacyPropertyAddOperationTransformer()))
          .inheritResourceAttributeDefinitions();

      builder.setCustomResourceTransformer(new LegacyPropertyResourceTransformer());
    }

    StoreResourceDefinition.buildTransformation(version, builder);
  }
  static void buildTransformation(
      ModelVersion version, ResourceTransformationDescriptionBuilder parent) {
    ResourceTransformationDescriptionBuilder builder =
        InfinispanModel.VERSION_4_0_0.requiresTransformation(version)
            ? parent.addChildRedirection(PATH, LEGACY_PATH)
            : parent.addChildResource(PATH);

    if (InfinispanModel.VERSION_4_0_0.requiresTransformation(version)) {
      builder.setCustomResourceTransformer(
          new ResourceTransformer() {
            @Override
            public void transformResource(
                ResourceTransformationContext context, PathAddress address, Resource resource)
                throws OperationFailedException {
              final ModelNode model = resource.getModel();

              final ModelNode binaryTableModel =
                  Resource.Tools.readModel(
                      resource.removeChild(BinaryTableResourceDefinition.PATH));
              if (binaryTableModel != null && binaryTableModel.isDefined()) {
                model
                    .get(DeprecatedAttribute.TABLE.getDefinition().getName())
                    .set(binaryTableModel);
              }

              final ModelNode properties =
                  model.remove(
                      StoreResourceDefinition.Attribute.PROPERTIES.getDefinition().getName());
              final ResourceTransformationContext childContext =
                  context.addTransformedResource(PathAddress.EMPTY_ADDRESS, resource);

              LegacyPropertyResourceTransformer.transformPropertiesToChildrenResources(
                  properties, address, childContext);

              context.processChildren(resource);
            }
          });
    }

    BinaryTableResourceDefinition.buildTransformation(version, builder);

    JDBCStoreResourceDefinition.buildTransformation(version, builder);
  }
 DeprecatedAttribute(
     String name, org.jboss.as.clustering.controller.Attribute[]... attributeSets) {
   int size = 0;
   for (org.jboss.as.clustering.controller.Attribute[] attributes : attributeSets) {
     size += attributes.length;
   }
   List<AttributeDefinition> definitions = new ArrayList<>(size);
   for (org.jboss.as.clustering.controller.Attribute[] attributes : attributeSets) {
     for (org.jboss.as.clustering.controller.Attribute attribute : attributes) {
       definitions.add(attribute.getDefinition());
     }
   }
   this.definition =
       ObjectTypeAttributeDefinition.Builder.of(
               name, definitions.toArray(new AttributeDefinition[size]))
           .setAllowNull(true)
           .setDeprecated(InfinispanModel.VERSION_4_0_0.getVersion())
           .setSuffix("table")
           .build();
 }