Skip to content

Fix trait codegen for list with uniqueitems trait #2706

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

joewyz
Copy link
Contributor

@joewyz joewyz commented Jul 17, 2025

Background

The current trait codegen is generating incorrect code for list with @uniqueItems. We use Set to represent this kind of list in generated Java code but some part of code still generate List related code.

For example, the following model:

$version: "2.0"

namespace example.traits

@trait
@uniqueItems
list MyListTrait {
    member: MyMemberList
}

@uniqueItems
list MyMemberList {
    member: String
}

will generate incorrect fromNode method because it still use Collection.toList()

public static MyListTrait fromNode(Node node) {
        Builder builder = builder();
        node.expectArrayNode()
            .getElements().stream()
            .map(n -> n.expectArrayNode()
                .getElements().stream()
                .map(n1 -> n1.expectStringNode().getValue())
                .collect(Collectors.toList()))
            .forEach(builder::addValues);
        return builder.build();
}

Similarly, if we have a list of unique items in a structure:

$version: "2.0"

namespace example.traits

@trait
structure MyStruct {
    member: MyMemberList
}

@uniqueItems
list MyMemberList {
    member: String
}

The generated Java code also failed because fromNode did not convert List to Set and getMemberOrEmpty returned emptyList

private final Set<String> member;
public static MyStructTrait fromNode(Node node) {
        Builder builder = builder();
        node.expectObjectNode()
            .expectArrayMember("member", n -> n.expectStringNode().getValue(), builder::member);

        return builder.build();
}

 public Set<String> getMemberOrEmpty() {
        return member == null ? Collections.emptyList() : member;
}

public static final class Builder extends AbstractTraitBuilder<MyStructTrait, Builder> {
        private final BuilderRef<Set<String>> member = BuilderRef.forOrderedSet();

        private Builder() {}

        public Builder member(Set<String> member) {
            clearMember();
            this.member.get().addAll(member);
            return this;
        }
}

This PR fixed the above problems.


By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.

@joewyz joewyz requested a review from a team as a code owner July 17, 2025 17:35
@joewyz joewyz requested a review from sugmanue July 17, 2025 17:35
@sugmanue
Copy link
Contributor

Let's make sure that we keep ordering of maps and sets of traits. That means to use a LinkedHashSet and LinkedHashMap for Set's and Map's respectively that would preserve the iteration order of how those were read or built.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants