class UserDefinedFunctionsMetadata extends AbstractNamedDiffable<Metadata.Custom> implements Metadata.Custom {

    public static final String TYPE = "user_defined_functions";

    private final List<UserDefinedFunctionMetadata> functionsMetadata;

    private UserDefinedFunctionsMetadata(List<UserDefinedFunctionMetadata> functions) {
        this.functionsMetadata = functions;
    }

    public static UserDefinedFunctionsMetadata newInstance(UserDefinedFunctionsMetadata instance) {
        return new UserDefinedFunctionsMetadata(new ArrayList<>(instance.functionsMetadata));
    }

    @VisibleForTesting
    public static UserDefinedFunctionsMetadata of(UserDefinedFunctionMetadata... functions) {
        return new UserDefinedFunctionsMetadata(Arrays.asList(functions));
    }

    public void add(UserDefinedFunctionMetadata function) {
        functionsMetadata.add(function);
    }

    public void replace(UserDefinedFunctionMetadata function) {
        for (int i = 0; i < functionsMetadata.size(); i++) {
            if (functionsMetadata.get(i).sameSignature(function.schema(), function.name(), function.argumentTypes())) {
                functionsMetadata.set(i, function);
            }
        }
    }

    public boolean contains(String schema, String name, List<DataType<?>> types) {
        for (UserDefinedFunctionMetadata function : functionsMetadata) {
            if (function.sameSignature(schema, name, types)) {
                return true;
            }
        }
        return false;
    }

    public void remove(String schema, String name, List<DataType<?>> types) {
        for (ListIterator<UserDefinedFunctionMetadata> iter = functionsMetadata.listIterator(); iter.hasNext(); ) {
            if (iter.next().sameSignature(schema, name, types)) {
                iter.remove();
            }
        }
    }

    public List<UserDefinedFunctionMetadata> functionsMetadata() {
        return functionsMetadata;
    }

    @Override
    public void writeTo(StreamOutput out) throws IOException {
        out.writeVInt(functionsMetadata.size());
        for (UserDefinedFunctionMetadata function : functionsMetadata) {
            function.writeTo(out);
        }
    }

    public UserDefinedFunctionsMetadata(StreamInput in) throws IOException {
        int size = in.readVInt();
        List<UserDefinedFunctionMetadata> functions = new ArrayList<>(size);
        for (int i = 0; i < size; i++) {
            functions.add(new UserDefinedFunctionMetadata(in));
        }
        this.functionsMetadata = functions;
    }

    @Override
    public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
        builder.startArray("functions");
        for (UserDefinedFunctionMetadata function : functionsMetadata) {
            function.toXContent(builder, params);
        }
        builder.endArray();
        return builder;
    }

    public static UserDefinedFunctionsMetadata fromXContent(XContentParser parser) throws IOException {
        List<UserDefinedFunctionMetadata> functions = new ArrayList<>();
        while (parser.nextToken() != XContentParser.Token.END_OBJECT) {
            if (parser.currentToken() == XContentParser.Token.FIELD_NAME && Objects.equals(parser.currentName(), "functions")) {
                if (parser.nextToken() == XContentParser.Token.START_ARRAY) {
                    while (parser.nextToken() != XContentParser.Token.END_ARRAY) {
                        functions.add(UserDefinedFunctionMetadata.fromXContent(parser));
                    }
                }
            }
        }
        return new UserDefinedFunctionsMetadata(functions);
    }

    @Override
    public EnumSet<Metadata.XContentContext> context() {
        return EnumSet.of(Metadata.XContentContext.GATEWAY, Metadata.XContentContext.SNAPSHOT);
    }

    @Override
    public boolean equals(Object o) {<FILL_FUNCTION_BODY>}

    @Override
    public int hashCode() {
        return Objects.hash(functionsMetadata);
    }

    @Override
    public String getWriteableName() {
        return TYPE;
    }

    @Override
    public Version getMinimalSupportedVersion() {
        return Version.V_3_0_1;
    }
}

        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        UserDefinedFunctionsMetadata that = (UserDefinedFunctionsMetadata) o;
        return functionsMetadata.equals(that.functionsMetadata);
    
<methods>public non-sealed void <init>() ,public Diff<org.elasticsearch.cluster.metadata.Metadata.Custom> diff(org.elasticsearch.cluster.metadata.Metadata.Custom) ,public org.elasticsearch.cluster.metadata.Metadata.Custom get() ,public static NamedDiff<T> readDiffFrom(Class<? extends T>, java.lang.String, org.elasticsearch.common.io.stream.StreamInput) throws java.io.IOException<variables>