Hi Nikita, hi Michael,

I took some days to get a deeper grasp of what I’m dealing with so that 
hopefully I don’t say something stupid.

Thank you for pointing out the existence of a the ExtendedTypeFactory class. I 
had missed it entirely and I can see its purpose now.

I still can’t properly overcome the issue of the non-extendable EnumSet class.

The problem is mainly that I need to somehow bring along the Enum type as it is 
needed to instantiate an EnumSet. You can’t just do new EnumSet(). The only way 
is to call static EnumSet.noneOf(Class<E> enumType). So enumType must be known.

For regular collections, what I do is to extend an existing collections like 
this:

public class StringList extends ArrayList<String>
public class StringSet extends HashSet<String>

Is that the intended way to deal with a collection-like object? 

Anyway, I can’t extend EnumSet so that solution is not on the table.

What I did try is to use a fake cover-up class that DOES NOT extend EnumSet. I 
created:

public abstract static class FakeEnumSet<E extends Enum<E>> extends HashSet<E>

And then for each Enum I created a placeholder class that extends FakeEnumSet 
and use that one in the model. This way I can either register directly or via 
factory and infer the Enum type. That’s ugly.

I also hit another wall in the implementation of my EnumSetExtendedType class. 
Assuming the class is aware of the right Enum type via subclassing, I still 
can’t return an EnumSet as the result of materializeObject(). If I do so then 
later on the class generated stuff from the modeller attempts to cast the value 
into the one indicated in the model and fails because EnumSet cannot be casted 
into FakeEnumSet.
This last thing I might be able to workaround by playing with the class 
templates, haven’t tried yet.

For now the only solution I managed to get working is to actually instantiate 
FakeEnumSet and then convert it from there to EnumSet later in the code. Not 
ideal because I’m basically doing things two times.

Overall what I feel is missing is the option to use a generic-like syntax in 
the modeller. If we could write java.util.EnumSet<Color> as the class and then 
get this Color bit later in the ExtendedTypeFactory, then we would be able to 
instantiate the EnumSet and populate it with the right Enum entries.

More than for just the EnumSet case, it would work wonders for ALL the 
Collections in general. Think things like these that I use all the time

java.util.List<String> for list of strings
java.util.List<Integer> for list of numbers
java.util.List<LocalDateTime> for list of dates

No need to create cover-up classes or extend anything.

I was looking at the Cayenne source for the:

protected ExtendedType createType(String className) in ExtendedTypeMap

It converts the className to a Class object and then invokes all the 
ExtendedTypeFactories with it. If it passed the string instead of the class the 
factory would have the option of processing the type between < > and then strip 
it.

Is it difficult to implement? Am I being naive?

Thank you,
Riccardo

> On 7 Apr 2025, at 17:58, Nikita Timofeev <ntimof...@objectstyle.com> wrote:
> 
> Hello Riccardo,
> 
> It should be possible to support EnumSet with ExtendedType.
> I did some prototyping, and it looks like you need to implement these parts:
> 
> 1. an ExtendedTypeFactory as EnumSet has internal subclasses (that also
> prevents using ValueType for this):
> 
> public ExtendedType getType(Class<?> objectClass) {
>  if(EnumSet.class.isAssignableFrom(objectClass)) {
>      return new EnumSetType();
>  }
>  return null;
> }
> 
> 2. an ExtendedType should deal with the serialization to/from the actual
> datatype you are using in the DB:
> 
> public class EnumSetType implements ExtendedType<EnumSet> {
>  @Override
>  public void setJdbcObject(PreparedStatement statement, EnumSet value, int
> pos, int type, int scale) throws Exception {
>    statement.setString(pos, toString(value));
>  }
> 
>  @Override
>  public EnumSet<?> materializeObject(ResultSet rs, int index, int type)
> throws Exception {
>    return fromString(rs.getString(index));
>  }
> 
>  String toString(EnumSet value) {
>    // probably should account for the Enum type
>  }
> 
>  EnumSet fromString(String value) {
>  }
> }
> 
> 3. and finally register factory with the runtime:
> 
> ServerModule.contributeTypeFactories(binder)
>        .add(new EnumSetFactory())
> 
> Hope this helps!
> 
> On Sat, Apr 5, 2025 at 8:03 PM Riccardo De Menna <deme...@tuorlo.net> wrote:
> 
>> Hello,
>> 
>> I was wondering if there is a way to add a custom data type for a non
>> extendable class. Specifically I’m thinking of java.util.EnumSet.
>> 
>> I often use Enums in my object classes and I occasionally employ EnumSets
>> when the case requires it. Unfortunately EnumSet is non extendable so I
>> can’t define a subclass and then use that as java type in the modeller.
>> 
>> Is there a way to hack the runtime into returning an EnumSet using a
>> custom ValueType or ExtenedType for multiple attributes (it does work if
>> there’s only one attribute using EnumSet but as soon as you define two the
>> runtime gets confused).
>> 
>> At the moment I’m just extending a HashSet<E extends Enum<E>> as a
>> workaround but every time I see that I feel guilty not being able to use
>> the ‘proper’ set collection for enums.
>> 
>> Thank you,
>> Riccardo De Menna
> 
> 
> 
> -- 
> Best regards,
> Nikita Timofeev

Reply via email to