Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generate annotations for Java classes #191

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 48 additions & 14 deletions shared/src/main/scala/io/kaitai/struct/ClassCompiler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ class ClassCompiler(

if (!lang.innerDocstrings)
compileClassDoc(curClass)
lang.classAnnotation(curClass)
lang.classHeader(curClass.name)
if (lang.innerDocstrings)
compileClassDoc(curClass)
Expand Down Expand Up @@ -77,14 +78,17 @@ class ClassCompiler(

// Attributes declarations and readers
val allAttrs: List[MemberSpec] =
curClass.seq ++
curClass.params ++
List(
AttrSpec(List(), RootIdentifier, CalcUserType(topClassName, None)),
AttrSpec(List(), ParentIdentifier, curClass.parentType)
) ++
ExtraAttrs.forClassSpec(curClass, lang)
compileIndexedAttrDeclarations(curClass.seq)
compileIndexedAttrDeclarations(curClass.params)
compileAttrDeclarations(allAttrs)

compileIndexedAttrReaders(curClass.seq)
compileIndexedAttrReaders(curClass.params)
compileAttrReaders(allAttrs)

curClass.toStringExpr.foreach(expr => lang.classToString(expr))
Expand Down Expand Up @@ -177,12 +181,7 @@ class ClassCompiler(
*/
def compileAttrDeclarations(attrs: List[MemberSpec]): Unit = {
attrs.foreach { (attr) =>
val isNullable = if (lang.switchBytesOnlyAsRaw) {
attr.isNullableSwitchRaw
} else {
attr.isNullable
}
lang.attributeDeclaration(attr.id, attr.dataTypeComposite, isNullable)
lang.attributeDeclaration(attr.id, attr.dataTypeComposite, isNullable(attr))
}
}

Expand All @@ -196,12 +195,45 @@ class ClassCompiler(
// FIXME: Python should have some form of attribute docs too
if (!attr.doc.isEmpty && !lang.innerDocstrings)
lang.attributeDoc(attr.id, attr.doc)
val isNullable = if (lang.switchBytesOnlyAsRaw) {
attr.isNullableSwitchRaw
} else {
attr.isNullable
}
lang.attributeReader(attr.id, attr.dataTypeComposite, isNullable)
lang.attributeReader(attr.id, attr.dataTypeComposite, isNullable(attr))
}
}

/**
* Iterates over a given list of attributes and generates attribute
* declarations for each of them, additionally annotate each field.
*
* @param attrs attribute list to traverse
*/
def compileIndexedAttrDeclarations(attrs: List[MemberSpec]): Unit = {
attrs.zipWithIndex.foreach { case (attr, index) =>
lang.attributeAnnotation(index, attr)
lang.attributeDeclaration(attr.id, attr.dataTypeComposite, isNullable(attr))
}
}

/**
* Iterates over a given list of attributes and generates attribute
* readers (AKA getters) for each of them, additionally annotate each
* getter.
*
* @param attrs attribute list to traverse
*/
def compileIndexedAttrReaders(attrs: List[MemberSpec]): Unit = {
attrs.zipWithIndex.foreach { case (attr, index) =>
// FIXME: Python should have some form of attribute docs too
if (!attr.doc.isEmpty && !lang.innerDocstrings)
lang.attributeDoc(attr.id, attr.doc)
lang.attributeAnnotation(index, attr)
lang.attributeReader(attr.id, attr.dataTypeComposite, isNullable(attr))
}
}

def isNullable(attr: MemberSpec): Boolean = {
if (lang.switchBytesOnlyAsRaw) {
attr.isNullableSwitchRaw
} else {
attr.isNullable
}
}

Expand Down Expand Up @@ -311,6 +343,7 @@ class ClassCompiler(

if (!lang.innerDocstrings)
compileInstanceDoc(instName, instSpec)
lang.instanceAnnotation(instName, instSpec, true)
lang.instanceHeader(className, instName, dataType, instSpec.isNullable)
if (lang.innerDocstrings)
compileInstanceDoc(instName, instSpec)
Expand Down Expand Up @@ -339,6 +372,7 @@ class ClassCompiler(
} else {
instSpec.isNullable
}
lang.instanceAnnotation(instName, instSpec, false)
lang.instanceDeclaration(instName, instSpec.dataTypeComposite, isNullable)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -502,9 +502,9 @@ class CSharpCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig)

//</editor-fold>

override def instanceDeclaration(attrName: InstanceIdentifier, attrType: DataType, isNullable: Boolean): Unit = {
out.puts(s"private bool ${flagForInstName(attrName)};")
out.puts(s"private ${kaitaiType2NativeTypeNullable(attrType, isNullable)} ${privateMemberName(attrName)};")
override def instanceDeclaration(instName: InstanceIdentifier, attrType: DataType, isNullable: Boolean): Unit = {
out.puts(s"private bool ${flagForInstName(instName)};")
out.puts(s"private ${kaitaiType2NativeTypeNullable(attrType, isNullable)} ${privateMemberName(instName)};")
}

override def instanceHeader(className: String, instName: InstanceIdentifier, dataType: DataType, isNullable: Boolean): Unit = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -822,11 +822,11 @@ class CppCompiler(

override def switchBytesOnlyAsRaw = true

override def instanceDeclaration(attrName: InstanceIdentifier, attrType: DataType, isNullable: Boolean): Unit = {
override def instanceDeclaration(instName: InstanceIdentifier, attrType: DataType, isNullable: Boolean): Unit = {
ensureMode(PrivateAccess)
outHdr.puts(s"bool ${calculatedFlagForName(attrName)};")
outHdr.puts(s"${kaitaiType2NativeType(attrType)} ${privateMemberName(attrName)};")
declareNullFlag(attrName, isNullable)
outHdr.puts(s"bool ${calculatedFlagForName(instName)};")
outHdr.puts(s"${kaitaiType2NativeType(attrType)} ${privateMemberName(instName)};")
declareNullFlag(instName, isNullable)
}

override def instanceHeader(className: List[String], instName: InstanceIdentifier, dataType: DataType, isNullable: Boolean): Unit = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -477,9 +477,9 @@ class GoCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig)
out.inc
}

override def instanceDeclaration(attrName: InstanceIdentifier, attrType: DataType, isNullable: Boolean): Unit = {
out.puts(s"${calculatedFlagForName(attrName)} bool")
out.puts(s"${idToStr(attrName)} ${kaitaiType2NativeType(attrType)}")
override def instanceDeclaration(instName: InstanceIdentifier, attrType: DataType, isNullable: Boolean): Unit = {
out.puts(s"${calculatedFlagForName(instName)} bool")
out.puts(s"${idToStr(instName)} ${kaitaiType2NativeType(attrType)}")
}

override def instanceHeader(className: List[String], instName: InstanceIdentifier, dataType: DataType, isNullable: Boolean): Unit = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,21 @@ class JavaCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig)
out.puts
}

override def classAnnotation(spec: ClassSpec): Unit = {
if (!spec.isTopLevel) return;

importList.add("io.kaitai.struct.annotations.Generated")

out.puts("@Generated(")
out.inc
out.puts(s"""id = "${spec.name.last}",""")
out.puts(s"""version = "${KSVersion.current}",""")
out.puts(s"""posInfo = ${config.readStoresPos},""")
out.puts(s"""autoRead = ${config.autoRead}""")
out.dec
out.puts(")")
}

override def classHeader(name: String): Unit = {
val staticStr = if (out.indentLevel > 0) {
"static "
Expand Down Expand Up @@ -197,6 +212,32 @@ class JavaCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig)

override def readFooter(): Unit = universalFooter

override def attributeAnnotation(index: Int, attr: MemberSpec): Unit = {
attr match {
case param: ParamDefSpec => {
importList.add("io.kaitai.struct.annotations.Parameter")

out.puts(s"""@Parameter(index = ${index}, id = "${param.id.humanReadable}")""")
}
case field: AttrSpec => {
field.id match {
case NamedIdentifier(name) => {
importList.add("io.kaitai.struct.annotations.SeqItem")

out.puts(s"""@SeqItem(index = ${index}, id = "${name}")""")
}
case NumberedIdentifier(_) => {
importList.add("io.kaitai.struct.annotations.SeqItem")

out.puts(s"""@SeqItem(index = ${index})""")
}
case _ =>
}
}
case _ =>
}
}

override def attributeDeclaration(attrName: Identifier, attrType: DataType, isNullable: Boolean): Unit = {
out.puts(s"private ${kaitaiType2JavaType(attrType, isNullable)} ${idToStr(attrName)};")
}
Expand Down Expand Up @@ -660,8 +701,14 @@ class JavaCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig)

//</editor-fold>

override def instanceDeclaration(attrName: InstanceIdentifier, attrType: DataType, isNullable: Boolean): Unit = {
out.puts(s"private ${kaitaiType2JavaTypeBoxed(attrType)} ${idToStr(attrName)};")
override def instanceAnnotation(instName: InstanceIdentifier, spec: InstanceSpec, getter: Boolean): Unit = {
importList.add("io.kaitai.struct.annotations.Instance")

out.puts(s"""@Instance(id = "${instName.name}")""")
}

override def instanceDeclaration(instName: InstanceIdentifier, attrType: DataType, isNullable: Boolean): Unit = {
out.puts(s"private ${kaitaiType2JavaTypeBoxed(attrType)} ${idToStr(instName)};")
}

override def instanceHeader(className: String, instName: InstanceIdentifier, dataType: DataType, isNullable: Boolean): Unit = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,9 +127,9 @@ class NimCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig)
override def attributeDeclaration(attrName: Identifier, attrType: DataType, isNullable: Boolean): Unit = {
out.puts(s"`${idToStr(attrName)}`*: ${ksToNim(attrType)}")
}
override def instanceDeclaration(attrName: InstanceIdentifier, attrType: DataType, isNullable: Boolean): Unit = {
out.puts(s"`${idToStr(attrName)}`: ${ksToNim(attrType)}")
out.puts(s"`${instanceFlagIdentifier(attrName)}`: bool")
override def instanceDeclaration(instName: InstanceIdentifier, attrType: DataType, isNullable: Boolean): Unit = {
out.puts(s"`${idToStr(instName)}`: ${ksToNim(attrType)}")
out.puts(s"`${instanceFlagIdentifier(instName)}`: bool")
}
override def attributeReader(attrName: Identifier, attrType: DataType, isNullable: Boolean): Unit = {}
override def classConstructorHeader(name: List[String], parentType: DataType, rootClassName: List[String], isHybrid: Boolean, params: List[ParamDefSpec]): Unit = {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -463,16 +463,16 @@ class RustCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig)
out.puts("}")
}

override def instanceDeclaration(attrName: InstanceIdentifier,
override def instanceDeclaration(instName: InstanceIdentifier,
attrType: DataType,
isNullable: Boolean): Unit = {
val typeName = kaitaiTypeToNativeType(
Some(attrName),
Some(instName),
typeProvider.nowClass,
attrType
)
out.puts(s"${calculatedFlagForName(attrName)}: Cell<bool>,")
out.puts(s"${idToStr(attrName)}: RefCell<$typeName>,")
out.puts(s"${calculatedFlagForName(instName)}: Cell<bool>,")
out.puts(s"${idToStr(instName)}: RefCell<$typeName>,")
}

def calculatedFlagForName(ksName: Identifier) =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,14 @@ abstract class LanguageCompiler(
def externalTypeDeclaration(extType: ExternalType): Unit = {}

def classDoc(name: List[String], doc: DocSpec): Unit = {}
/**
* Generates additional meta-information for attribute, such as annotations in Java
* or attributes in C#. That annotations appears between documentation and main body
* of class.
*
* @param spec Specification of type
*/
def classAnnotation(spec: ClassSpec): Unit = {}
def classHeader(name: List[String]): Unit
def classFooter(name: List[String]): Unit
def classForwardDeclaration(name: List[String]): Unit = {}
Expand Down Expand Up @@ -114,6 +122,15 @@ abstract class LanguageCompiler(
*/
def readFooter(): Unit

/**
* Generates additional meta-information for an attribute or type parameter,
* such as annotations in Java or attributes in C#.
*
* @param index Sequential index of `seq` attribute/parameter in type, started from 0.
* Attributes and parameters have independent indexes
* @param attr Specification of attribute
*/
def attributeAnnotation(index: Int, attr: MemberSpec): Unit = {}
def attributeDeclaration(attrName: Identifier, attrType: DataType, isNullable: Boolean): Unit
def attributeReader(attrName: Identifier, attrType: DataType, isNullable: Boolean): Unit
def attributeDoc(id: Identifier, doc: DocSpec): Unit = {}
Expand Down Expand Up @@ -151,10 +168,20 @@ abstract class LanguageCompiler(
def popPos(io: String): Unit
def alignToByte(io: String): Unit

/**
* Generates additional meta-information for instance, such as annotations in Java
* or attributes in C#.
*
* @param instName Name of instance
* @param spec Specification of instance
* @param getter If `true`, method called for annotate value getter, otherwise for annotate storage variable
*/
def instanceAnnotation(instName: InstanceIdentifier, spec: InstanceSpec, getter: Boolean): Unit = {}
def instanceDeclHeader(className: List[String]): Unit = {}
def instanceClear(instName: InstanceIdentifier): Unit = {}
def instanceSetCalculated(instName: InstanceIdentifier): Unit = {}
def instanceDeclaration(attrName: InstanceIdentifier, attrType: DataType, isNullable: Boolean): Unit = attributeDeclaration(attrName, attrType, isNullable)
def instanceDeclaration(instName: InstanceIdentifier, attrType: DataType, isNullable: Boolean): Unit =
attributeDeclaration(instName, attrType, isNullable)
def instanceHeader(className: List[String], instName: InstanceIdentifier, dataType: DataType, isNullable: Boolean): Unit
def instanceFooter: Unit
def instanceCheckCacheAndReturn(instName: InstanceIdentifier, dataType: DataType): Unit
Expand Down
Loading