Unfortunately never had any tasks that would require storing collections as an attribute, in general that's a relationship task, so not in a position to recommend anything particular.
But here are some options that I see in this case: - Create some sort of wrapper class like EnumSetHolder that will be used instead of extending EnumSet directly. It could store type and EnumSet or there could be multiple implementations that will define type and properly initialize EnumSet. - Use some reflection and get Enum type from the EnumSet implementation and store it with the actual value in the DB and get it on read to create proper EnumSet. I did some prototyping for that and it seems to be working fine (except for the ugly part of getting Enum type and storing it in the DB). - Use generics in the type name as you suggest and customize Cayenne internals to properly deal with it. This seems like a pretty complex task, as this type could be used in other places. You could start by replacing ExtendedTypeMap with some custom implementation to support that String to ExtendedType getter (but it's not that clean as you also will need a custom JdbcAdapter for that to work) and go from there. Also I want to mention here that the Java implementation of generics most likely could be in the way too, as generics type is just not there in many cases. On Mon, Apr 14, 2025 at 2:49 PM Riccardo De Menna <deme...@tuorlo.net> wrote: > 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 > > -- Best regards, Nikita Timofeev