Skip to content

Commit 8902e87

Browse files
committed
Generate enums as interface + enum + class for unknown values
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 enum `enum` would be generated as: ```java // interface to abstract known and unknown values interface IEnum extends IKaitaiEnum { // Storage for unknown values public static class Unknown extends IKaitaiEnum.Unknown implements IEnum { Unknown(long id) { super(id); } @OverRide public String toString() { return "Enum(" + this.id + ")"; } @OverRide public boolean equals(Object other) { return other instanceof Enum && this.id == ((Enum)other).id; } } } // Storage for known values class Enum implements IEnum { VARIANT_1(1), VARIANT_2(2), VARIANT_3(3); private final long id; private static HashMap<Long, IEnum> variants = new HashMap<>(3); static { for (final Enum e : values()) { variants.put(e.id, e); } } public static IEnum byId(final long id) { return variants.computeIfAbsent(id, _id -> new IEnum.Unknown(id)); } private Enum(long id) { this.id = id; } @OverRide public long id() { return this.id; } } ```
1 parent 3c855b3 commit 8902e87

File tree

1 file changed

+64
-16
lines changed

1 file changed

+64
-16
lines changed

shared/src/main/scala/io/kaitai/struct/languages/JavaCompiler.scala

Lines changed: 64 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -535,21 +535,35 @@ class JavaCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig)
535535
// doing this workaround for enums.
536536

537537
val onType = typeProvider._currentSwitchType.get
538-
val isNullable = onType match {
538+
val isEnum = onType match {
539539
case _: EnumType => true
540540
case _ => false
541541
}
542542

543-
if (isNullable) {
544-
val nameSwitchStr = expression(NAME_SWITCH_ON)
543+
if (isEnum) {
544+
val javaEnumName = kaitaiType2JavaType(onType)
545+
// Open scope for "on" isolation
545546
out.puts("{")
546547
out.inc
547-
out.puts(s"${kaitaiType2JavaType(onType)} $nameSwitchStr = ${expression(on)};")
548-
out.puts(s"if ($nameSwitchStr != null) {")
548+
out.puts(s"final I$javaEnumName on = ${expression(on)};")
549+
out.puts(s"if (on instanceof $javaEnumName) {")
550+
out.inc
551+
out.puts(s"switch (($javaEnumName)on) {")
549552
out.inc
550553

551-
super.switchCasesRender(id, on, cases, normalCaseProc, elseCaseProc)
554+
cases.foreach { case (condition, result) =>
555+
condition match {
556+
case SwitchType.ELSE_CONST =>
557+
// skip for now
558+
case _ =>
559+
switchCaseStart(condition)
560+
normalCaseProc(result)
561+
switchCaseEnd()
562+
}
563+
}
552564

565+
out.dec
566+
out.puts("} // switch")
553567
out.dec
554568
cases.get(SwitchType.ELSE_CONST) match {
555569
case Some(result) =>
@@ -562,6 +576,7 @@ class JavaCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig)
562576
out.puts("}")
563577
}
564578

579+
// Close "on" isolation scope
565580
out.dec
566581
out.puts("}")
567582
} else {
@@ -700,8 +715,32 @@ class JavaCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig)
700715
override def enumDeclaration(curClass: String, enumName: String, enumColl: Seq[(Long, String)]): Unit = {
701716
val enumClass = type2class(enumName)
702717

718+
// Enums are always contained in some other class so we can generate two public items
719+
720+
// public interface I<enum> extends IKaitaiEnum { ... }
703721
out.puts
704-
out.puts(s"public enum $enumClass {")
722+
out.puts(s"public interface I$enumClass extends IKaitaiEnum {")
723+
out.inc
724+
out.puts(s"public static class Unknown extends IKaitaiEnum.Unknown implements I$enumClass {")
725+
out.inc
726+
out.puts("Unknown(long id) { super(id); }")
727+
out.puts
728+
out.puts("@Override");
729+
out.puts(s"public String toString() { return \"${enumClass}(\" + this.id + \")\"; }");
730+
out.puts
731+
out.puts("@Override");
732+
out.puts(s"public boolean equals(Object other) {");
733+
out.inc
734+
out.puts("return other instanceof Unknown && this.id == ((Unknown)other).id;")
735+
out.dec
736+
out.puts("}");
737+
out.dec
738+
out.puts("}") // close class
739+
out.dec
740+
out.puts("}") // close interface
741+
742+
// public enum <enum> implements I<enum> { ... }
743+
out.puts(s"public enum $enumClass implements I$enumClass {")
705744
out.inc
706745

707746
if (enumColl.size > 1) {
@@ -716,23 +755,32 @@ class JavaCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig)
716755

717756
out.puts
718757
out.puts("private final long id;")
719-
out.puts(s"$enumClass(long id) { this.id = id; }")
720-
out.puts("public long id() { return id; }")
721-
out.puts(s"private static final Map<Long, $enumClass> byId = new HashMap<Long, $enumClass>(${enumColl.size});")
758+
out.puts(s"private static final HashMap<Long, I$enumClass> variants = new HashMap<>(${enumColl.size});")
722759
out.puts("static {")
723760
out.inc
724-
out.puts(s"for ($enumClass e : $enumClass.values())")
761+
out.puts(s"for ($enumClass e : values()) {")
725762
out.inc
726-
out.puts(s"byId.put(e.id(), e);")
763+
out.puts(s"variants.put(e.id, e);")
727764
out.dec
765+
out.puts("}")// for
728766
out.dec
729-
out.puts("}")
730-
out.puts(s"public static $enumClass byId(long id) { return byId.get(id); }")
767+
out.puts("}")// static initializer
768+
out.puts
769+
out.puts(s"public static I$enumClass byId(final long id) {")
770+
out.inc
771+
out.puts(s"return variants.computeIfAbsent(id, _id -> new I$enumClass.Unknown(id));")
731772
out.dec
732-
out.puts("}")
773+
out.puts("}")// byId(...)
774+
out.puts
775+
out.puts(s"private $enumClass(long id) { this.id = id; }")
776+
out.puts
777+
out.puts("@Override")
778+
out.puts("public long id() { return id; }")
779+
out.dec
780+
out.puts("}")// enum
733781

734-
importList.add("java.util.Map")
735782
importList.add("java.util.HashMap")
783+
importList.add("io.kaitai.struct.IKaitaiEnum")
736784
}
737785

738786
override def debugClassSequence(seq: List[AttrSpec]) = {

0 commit comments

Comments
 (0)