Skip to content

Commit

Permalink
Generate enums as interface + enum + class for unknown values
Browse files Browse the repository at this point in the history
The new generated enum is represented as an interface with two nested classes:
- enum class Known with the list of known enumeration values
- class Unknown which represents any not-known value

The enums `enum` would be generated as:

```java
interface Enum extends IKaitaiEnum {
    public static class Unknown extends IKaitaiEnum.Unknown implements Enum {
        private Unknown(long id) { super(id); }
    }
    public enum Known implements Enum {
        Variant1(1),
        Variant2(2),
        Variant(3);

        private final long id;

        static HashMap<Long, Enum> variants = new HashMap<>(3);
        static {
            for (final Known e : Known.values()) {
                variants.put(e.id, e);
            }
        }

        private Known(long id) { this.id = id; }
        @OverRide
        public long id() { return this.id; }
    }

    public static Enum byId(final long id) {
        return Known.variants.computeIfAbsent(id, _id -> new Unknown(id));
    }
}
```

Unfortunately, it is not possible to generate enum what will implement nested interface
due to cyclic reference. If that would be possible, we would be protected against name clashes.
In the current implementation the names Known and Unknown can clash with enum name itself
  • Loading branch information
Mingun committed Mar 25, 2024
1 parent d105f21 commit 4e19179
Show file tree
Hide file tree
Showing 2 changed files with 31 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -690,7 +690,15 @@ class JavaCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig)
val enumClass = type2class(enumName)

out.puts
out.puts(s"public enum $enumClass {")
out.puts(s"public interface $enumClass implements IKaitaiEnum {")
out.inc
out.puts(s"public static class Unknown extends IKaitaiEnum.Unknown implements $enumClass {")
out.inc
out.puts("Unknown(long id) { super(id); }")
out.dec
out.puts("}")
out.puts
out.puts(s"public enum Known implements $enumClass {")
out.inc

if (enumColl.size > 1) {
Expand All @@ -705,23 +713,37 @@ class JavaCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig)

out.puts
out.puts("private final long id;")
out.puts(s"$enumClass(long id) { this.id = id; }")
out.puts("public long id() { return id; }")
out.puts(s"private static final Map<Long, $enumClass> byId = new HashMap<Long, $enumClass>(${enumColl.size});")
out.puts(s"static final HashMap<Long, $enumClass> variants = new HashMap<>(${enumColl.size});")
out.puts("static {")
out.inc
out.puts(s"for ($enumClass e : $enumClass.values())")
out.puts(s"for ($enumClass e : $enumClass.values()) {")
out.inc
out.puts(s"byId.put(e.id(), e);")
out.puts(s"variants.put(e.id(), e);")
out.dec
out.puts("}")
out.dec
out.puts("}")
out.puts
out.puts("Known(long id) { this.id = id; }")
out.puts
out.puts("@Override")
out.puts("public long id() { return id; }")
out.puts
out.puts("@Override");
out.puts(s"public String toString() { return \"${$enumClass}(\" + this.id + \")>\"; }");
out.dec
out.puts("}")
out.puts
out.puts(s"public static $enumClass byId(final long id) {")
out.inc
out.puts("return Known.variants.computeIfAbsent(id, _id -> new Unknown(id));")
out.dec
out.puts("}")
out.puts(s"public static $enumClass byId(long id) { return byId.get(id); }")
out.dec
out.puts("}")

importList.add("java.util.Map")
importList.add("java.util.HashMap")
importList.add("io.kaitai.struct.IKaitaiEnum")
}

override def debugClassSequence(seq: List[AttrSpec]) = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ class JavaTranslator(provider: TypeProvider, importList: ImportList) extends Bas
s"${JavaCompiler.publicMemberName(id)}()"

override def doEnumByLabel(enumTypeAbs: List[String], label: String): String =
s"${enumClass(enumTypeAbs)}.${Utils.upperUnderscoreCase(label)}"
s"${enumClass(enumTypeAbs)}.Known.${Utils.upperUnderscoreCase(label)}"
override def doEnumById(enumTypeAbs: List[String], id: String): String =
s"${enumClass(enumTypeAbs)}.byId($id)"

Expand Down

0 comments on commit 4e19179

Please sign in to comment.