@@ -535,37 +535,48 @@ class JavaCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig)
535535 // doing this workaround for enums.
536536
537537 val onType = typeProvider._currentSwitchType.get
538- val isNullable = onType match {
539- case _ : EnumType => true
540- case _ => false
541- }
542-
543- if (isNullable) {
544- val nameSwitchStr = expression(NAME_SWITCH_ON )
545- out.puts(" {" )
546- out.inc
547- out.puts(s " ${kaitaiType2JavaType(onType)} $nameSwitchStr = ${expression(on)}; " )
548- out.puts(s " if ( $nameSwitchStr != null) { " )
549- out.inc
538+ onType match {
539+ case EnumType (name, owner, _) => {
540+ val enumName = types2class(owner :+ name)
541+ // Open scope for "on" isolation
542+ out.puts(" {" )
543+ out.inc
544+ out.puts(s " final ${enum2iface(name, owner)} on = ${expression(on)}; " )
545+ out.puts(s " if (on instanceof $enumName) { " )
546+ out.inc
547+ out.puts(s " switch (( $enumName)on) { " )
548+ out.inc
549+
550+ cases.foreach { case (condition, result) =>
551+ condition match {
552+ case SwitchType .ELSE_CONST =>
553+ // skip for now
554+ case _ =>
555+ switchCaseStart(condition)
556+ normalCaseProc(result)
557+ switchCaseEnd()
558+ }
559+ }
550560
551- super .switchCasesRender(id, on, cases, normalCaseProc, elseCaseProc)
561+ out.dec
562+ out.puts(" } // switch" )
563+ out.dec
564+ cases.get(SwitchType .ELSE_CONST ) match {
565+ case Some (result) =>
566+ out.puts(" } else {" )
567+ out.inc
568+ elseCaseProc(result)
569+ out.dec
570+ out.puts(" }" )
571+ case None =>
572+ out.puts(" }" )
573+ }
552574
553- out.dec
554- cases.get(SwitchType .ELSE_CONST ) match {
555- case Some (result) =>
556- out.puts(" } else {" )
557- out.inc
558- elseCaseProc(result)
559- out.dec
560- out.puts(" }" )
561- case None =>
562- out.puts(" }" )
575+ // Close "on" isolation scope
576+ out.dec
577+ out.puts(" }" )
563578 }
564-
565- out.dec
566- out.puts(" }" )
567- } else {
568- super .switchCasesRender(id, on, cases, normalCaseProc, elseCaseProc)
579+ case _ => super .switchCasesRender(id, on, cases, normalCaseProc, elseCaseProc)
569580 }
570581 }
571582
@@ -699,9 +710,44 @@ class JavaCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig)
699710
700711 override def enumDeclaration (curClass : String , enumName : String , enumColl : Seq [(Long , String )]): Unit = {
701712 val enumClass = type2class(enumName)
713+ val enumIface = enum2iface(enumName)
714+
715+ // Enums are always contained in some other class so we can generate two public items
702716
717+ // public interface I<enum> extends IKaitaiEnum { ... }
718+ out.puts
719+ out.puts(s " public interface $enumIface extends IKaitaiEnum { " )
720+ out.inc
721+ out.puts(s " public static final class Unknown extends IKaitaiEnum.Unknown implements $enumIface { " )
722+ out.inc
723+ out.puts(" Unknown(long id) { super(id); }" )
724+ out.puts
725+ out.puts(" @Override" );
726+ out.puts(s " public String toString() { return \" ${enumClass}( \" + this.id + \" ) \" ; } " );
703727 out.puts
704- out.puts(s " public enum $enumClass { " )
728+ out.puts(" @Override" );
729+ out.puts(s " public int hashCode() { " )
730+ out.inc
731+ // Use the hashCode implementation that is generated by Eclipse and VSCode Java plugin.
732+ // It uses prime 31 and this is how Arrays.hashCode() is implemented
733+ out.puts(s " final int result = 31 + \" ${enumClass}\" .hashCode(); " )
734+ out.puts(" return 31 * result + Long.hashCode(this.id);" )
735+ out.dec
736+ out.puts(" }" );
737+ out.puts
738+ out.puts(" @Override" );
739+ out.puts(s " public boolean equals(Object other) { " );
740+ out.inc
741+ out.puts(" return other instanceof Unknown && this.id == ((Unknown)other).id;" )
742+ out.dec
743+ out.puts(" }" );
744+ out.dec
745+ out.puts(" }" ) // close class
746+ out.dec
747+ out.puts(" }" ) // close interface
748+
749+ // public enum <enum> implements I<enum> { ... }
750+ out.puts(s " public enum $enumClass implements $enumIface { " )
705751 out.inc
706752
707753 if (enumColl.size > 1 ) {
@@ -716,23 +762,32 @@ class JavaCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig)
716762
717763 out.puts
718764 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}); " )
765+ out.puts(s " private static final HashMap<Long, $enumIface> variants = new HashMap<>( ${enumColl.size}); " )
722766 out.puts(" static {" )
723767 out.inc
724- out.puts(s " for ( $enumClass e : $enumClass . values())" )
768+ out.puts(s " for ( $enumClass e : values()) { " )
725769 out.inc
726- out.puts(s " byId .put(e.id() , e);" )
770+ out.puts(s " variants .put(e.id, e);" )
727771 out.dec
772+ out.puts(" }" )// for
728773 out.dec
729- out.puts(" }" )
730- out.puts(s " public static $enumClass byId(long id) { return byId.get(id); } " )
774+ out.puts(" }" )// static initializer
775+ out.puts
776+ out.puts(s " public static $enumIface byId(final long id) { " )
777+ out.inc
778+ out.puts(s " return variants.computeIfAbsent(id, _id -> new $enumIface.Unknown(id)); " )
731779 out.dec
732- out.puts(" }" )
780+ out.puts(" }" )// byId(...)
781+ out.puts
782+ out.puts(s " private $enumClass(long id) { this.id = id; } " )
783+ out.puts
784+ out.puts(" @Override" )
785+ out.puts(" public long id() { return id; }" )
786+ out.dec
787+ out.puts(" }" )// enum
733788
734- importList.add(" java.util.Map" )
735789 importList.add(" java.util.HashMap" )
790+ importList.add(" io.kaitai.struct.IKaitaiEnum" )
736791 }
737792
738793 override def debugClassSequence (seq : List [AttrSpec ]) = {
@@ -874,7 +929,7 @@ object JavaCompiler extends LanguageCompilerStatic
874929 case KaitaiStructType | CalcKaitaiStructType (_) => kstructName
875930
876931 case t : UserType => types2class(t.name)
877- case EnumType (name, owner, _) => types2class(owner :+ name)
932+ case EnumType (name, owner, _) => enum2iface( name, owner )
878933
879934 case _ : ArrayType => kaitaiType2JavaTypeBoxed(attrType, importList)
880935
@@ -918,7 +973,7 @@ object JavaCompiler extends LanguageCompilerStatic
918973 case KaitaiStructType | CalcKaitaiStructType (_) => kstructName
919974
920975 case t : UserType => types2class(t.name)
921- case EnumType (name, owner, _) => types2class(owner :+ name)
976+ case EnumType (name, owner, _) => enum2iface( name, owner )
922977
923978 case at : ArrayType => {
924979 importList.add(" java.util.ArrayList" )
@@ -931,6 +986,10 @@ object JavaCompiler extends LanguageCompilerStatic
931986
932987 def types2class (names : Iterable [String ]) = names.map(x => type2class(x)).mkString(" ." )
933988
989+ def enum2iface (name : String ) = s " I ${type2class(name)}"
990+ def enum2iface (name : String , owner : List [String ]): String =
991+ (owner.map(type2class) :+ enum2iface(name)).mkString(" ." )
992+
934993 override def kstreamName : String = " KaitaiStream"
935994 override def kstructName : String = " KaitaiStruct"
936995}
0 commit comments