How could I get Immutables to generate a class with this sort
public class IdentifiedUserDetails implements UserDetails, CredentialsContainer, Identified<UUID> {
private static final long serialVersionUID = 4905378177558522349L;
private final UUID id;
private final String username;
private final Set<GrantedAuthority> authorities;
private boolean accountNonExpired = true;
private boolean accountNonLocked = true;
private boolean credentialsNonExpired = true;
private boolean enabled = true;
private String password;
IdentifiedUserDetails( final UUID id, final String username, final String password ) {
this.id = Objects.requireNonNull( id );
this.username = Objects.requireNonNull( username );
this.password = Objects.requireNonNull( password );
this.authorities = Collections.unmodifiableSet( sortAuthorities( Collections.emptySet() ) );
}
private static SortedSet<GrantedAuthority> sortAuthorities(
final Collection<? extends GrantedAuthority> authorities ) {
// Ensure array iteration order is predictable (as per
// UserDetails.getAuthorities() contract and SEC-717)
return authorities.stream()
.filter( Objects::nonNull )
.collect( Collectors.toCollection( () -> {
return new TreeSet<GrantedAuthority>(
Comparator.nullsFirst( Comparator.comparing( GrantedAuthority::getAuthority ) ) );
} ) );
}
}
note: the empty set is a placeholder for a real set passed in from some external data source, I haven't started populating it yet but I will at some point, and the sort is a re-implementation of the one in spring security, you should presume that the sort needs to be applied to any set that would be passed to the Immutables builder.
There are many variations on how you can achieve that using Immutables annotation processor, given that I understand what you trying to achieve ;)
Immutables supports SortedSet
out of the box, but only using natural ordering (see @Value.NaturalOrder
and @Value.ReverseOrder
). If you want to apply special comparator, Immutables will only allow you to build the set yourself and set it onto a builder. Judging from the example, it is desirable that ordering would be something specific to the implementation of the object, so I'll skip to other options.
There's powerful (but somewhat error-prone) functionality to enable normalization/canonicalization of the object using @Value.Check
method. It is described in the guide: http://immutables.github.io/immutable.html#normalization. However, using the normalization is a bit complicated by the need to check if a set/collection is already sorted.
In the end, I would propose yet another, simpler approach, which I've used for similar purposes. @Value.Derived
annotation allows you to build an alternative view of a data during object construction. In this case there will be collection used as initialization buffer and the computed, alternative view of this data. Computation will happen during construction and an immutable object will never change after that. We'll play with access and attribute names to make it look nice. Here's the example:
@Value.Immutable public abstract class IdentifiedUserDetails implements UserDetails, CredentialsContainer, Identified<UUID> { private static final long serialVersionUID = 4905378177558522349L; public abstract UUID getId(); public abstract String getUsername(); public abstract String getPassword(); // other attributes omitted for brevity // ... abstract @SkipNulls List<GrantedAuthority> authority(); @Value.Derived public SortedSet<GrantedAuthority> getAuthorities() { return authority().stream() .collect(Collectors.toCollection(() -> { return new TreeSet( Comparator.nullsFirst(Comparator.comparing(GrantedAuthority::getAuthority))); })); } } public static void demonstration(GrantedAuthority ga1, GrantedAuthority ga2) { IdentifiedUserDetails details = ImmutableIdentifiedUserDetails.builder() .id(UUID.randomUUID()) .username("Name") .password("String") //... .addAuthority(ga1) .addAuthority(ga2) .build(); SortedSet<GrantedAuthority> sortedAuthorities = details.getAuthorities(); }
P.S. @SkipNulls
is a kind of BYOAnnotation. Create it, if needed, and it will be recognized by a simple name.