diff --git a/plugin/src/main/scala/com/typesafe/genjavadoc/AST.scala b/plugin/src/main/scala/com/typesafe/genjavadoc/AST.scala index ef46f53..4ac258a 100644 --- a/plugin/src/main/scala/com/typesafe/genjavadoc/AST.scala +++ b/plugin/src/main/scala/com/typesafe/genjavadoc/AST.scala @@ -56,6 +56,7 @@ trait AST { this: TransformCake => def classMembers = members.collect { case classInfo: ClassInfo => classInfo } def methodMembers = members.collect { case methodInfo: MethodInfo => methodInfo } + def fieldMembers = members.collect { case fieldInfo: FieldInfo => fieldInfo } override def toString = s"ClassInfo($name, ${pattern("XXXXX", "AAAAA")}, module=$module, pckg=$pckg, ${filepattern("FFFFFF")}, interface=$interface, static=$static)" + @@ -191,6 +192,55 @@ trait AST { this: TransformCake => } } + // for val, var and Constructor parameters + case class FieldInfo(access: String, pattern: String => String, ret: String, name: String, comment: Seq[String], v: Option[ValDef] = None) extends Templ { + def sig: String = { + s"$addAnnotations${pattern(s"$ret $name")}" + } + + private def addAnnotations: String = v match { + case Some(definition) => { + val annotations = definition.symbol.annotations + .filter(a => allowedAnnotations.contains(a.symbol.fullName('.'))) + .map { a => s"@${a.symbol.fullName('.')}" } + .mkString(System.lineSeparator()) + if (!annotations.isEmpty) { + annotations + System.lineSeparator() + } else { + annotations + } + } + case None => "" + } + } + + object FieldInfo { + def apply(v: ValDef, interface: Boolean, comment: Seq[String], deprecation: Option[DeprecationInfo]): FieldInfo = { + val acc = fieldAccess(v.symbol, interface) + fieldFlags(v.mods, interface) + + val (vt, name) = + if (v.name == nme.CONSTRUCTOR) { + ("", v.symbol.enclClass.name.toString) + } else (js(v.symbol, v.tpt.tpe), v.name.toString) + + val tp = v.symbol.owner.thisType.memberInfo(v.symbol) match { + case p@PolyType(params, _) => js(v.symbol, p) + case _ => "" + } + + val impl = if (v.mods.isDeferred || interface) ";" else "= null;" + val pattern = (n: String) => s"$acc $tp $n $impl" + + FieldInfo(acc, pattern, vt, name, comment, Some(v)) + } + + def apply(sym: Symbol): FieldInfo = { + val d = ValDef(sym, EmptyTree) + val f = FieldInfo(d, false, Nil, None) + f.copy(pattern = n => "static " + f.pattern(n)) + } + } + private def mangleMethodName(p: ValDef): String = { if (this.javaKeywords contains p.name.toString) s"${p.name}_" else p.name.toString } @@ -213,6 +263,14 @@ trait AST { this: TransformCake => else "public" // this is the case for interfaces } + private def fieldAccess(sym: Symbol, interface: Boolean): String = { + if (sym.isPublic) "public" + else if (sym.isProtected && !interface) "protected" + else if (sym.isPrivate && !interface) "private" + else if (strictVisibility && sym.privateWithin != NoSymbol) "" + else "public" // this is the case for interfaces + } + private def flags(m: Modifiers): String = { var f: List[String] = Nil if (m.isFinal) f ::= "final" @@ -227,4 +285,10 @@ trait AST { this: TransformCake => (if (f.nonEmpty) " " else "") + f.mkString(" ") } + private def fieldFlags(m: Modifiers, interface: Boolean): String = { + var f: List[String] = Nil + if (m.isFinal && !interface) f ::= "final" + (if (f.nonEmpty) " " else "") + f.mkString(" ") + } + } diff --git a/plugin/src/main/scala/com/typesafe/genjavadoc/BasicTransform.scala b/plugin/src/main/scala/com/typesafe/genjavadoc/BasicTransform.scala index 8d80fd3..8fcc996 100644 --- a/plugin/src/main/scala/com/typesafe/genjavadoc/BasicTransform.scala +++ b/plugin/src/main/scala/com/typesafe/genjavadoc/BasicTransform.scala @@ -45,6 +45,9 @@ trait BasicTransform { this: TransformCake => private def advancePos(p: Position) = if (p.isDefined && p > templateMaxPos) templateMaxPos = p + def createField: Boolean + def borrowConstructorArgsComment: Boolean + def newTransform(tree: Tree): Tree = { def commentText(tp: Position, endPos: Option[Position]) = { val ret = if (tp.isDefined) { @@ -110,7 +113,13 @@ trait BasicTransform { this: TransformCake => addMethod(d, text) } tree - case _: ValDef => { track(tree) } + case v: ValDef => { + if (createField) { + // addField(v, comments) // not working + addField(v.copy(), commentText(v.pos, endPos(v.rhs))) + } else track(tree) + tree + } case _: PackageDef => { track(tree); superTransform(tree) } case _: Template => { track(tree); superTransform(tree) } case _: TypeTree => { track(tree) } @@ -155,6 +164,37 @@ trait BasicTransform { this: TransformCake => clazz = clazz map (c => c.addMember(MethodInfo(d, c.interface, comment, hasVararg = true, deprecation = deprecationInfo(d)))) } + private def addField(v: ValDef, comments: Seq[String]): Unit = { + clazz = clazz map (c => { + def mergeComments(comments: Seq[String]): Seq[String] = { + val r = "[ ]?[*] @param[ ]+([a-zA-Z_$][a-zA-Z_$0-9]+)[ ]+.*".r + c.comment.collect { case co: String if co.contains("* @param ") => + try { + val r(pName) = co + if (!pName.isEmpty && pName == v.name.toString.trim) { + co.replace("@param ", "") + } else "" + } catch { + case ex: Throwable => "" + } + }.collect { case str: String if str != "" => str } match { + case Nil => comments + case fromCon => comments match { + case Nil => (Seq("/**") ++ fromCon) :+ " */" + case only if only.size == 1 && !only.exists(p => p.contains("/**") && p.contains("*/")) => + (only :+ "/**") ++ fromCon :+ " */" + case only if only.size == 1 && only.exists(p => p.contains("/**") && p.contains("*/")) => + Seq("/**") ++ fromCon :+ only.head.replace("/**", " * ") + case some => (Seq("/**") ++ fromCon :+ some.head.replace("/**", " *")) ++ some.tail + } + } + } + val fieldInfo = FieldInfo(v, c.interface, if (borrowConstructorArgsComment) mergeComments(comments) else comments, deprecation = deprecationInfo(v)) + c.addMember(fieldInfo) + }) + } + + private def deprecationInfo(v: ValDef): Option[DeprecationInfo] = deprecationInfo(v.symbol) private def deprecationInfo(d: DefDef): Option[DeprecationInfo] = deprecationInfo(d.symbol) private def deprecationInfo(d: ImplDef): Option[DeprecationInfo] = deprecationInfo(d.symbol) private def deprecationInfo(symbol: Symbol): Option[DeprecationInfo] = diff --git a/plugin/src/main/scala/com/typesafe/genjavadoc/Output.scala b/plugin/src/main/scala/com/typesafe/genjavadoc/Output.scala index b13d2a3..ed83bc5 100644 --- a/plugin/src/main/scala/com/typesafe/genjavadoc/Output.scala +++ b/plugin/src/main/scala/com/typesafe/genjavadoc/Output.scala @@ -25,6 +25,7 @@ trait Output { this: TransformCake => m match { case clazz: ClassInfo => write(out, clazz) case method: MethodInfo => write(out, method) + case field: FieldInfo => write(out, field) } out.outdent() out("}") @@ -34,6 +35,10 @@ trait Output { this: TransformCake => m.comment foreach (out(_)) out(m.sig) } + def write(out: Out, f: FieldInfo): Unit = { + f.comment foreach (out(_)) + out(f.sig) + } trait Out { var ind = 0 @@ -87,7 +92,7 @@ trait Output { this: TransformCake => } match { case Some(nestedClass: ClassInfo) => Seq( - c.copy(members = c.classMembers.filterNot(_ == nestedClass).flatMap(liftInnerClassesWithSameName) ++ c.methodMembers), + c.copy(members = c.classMembers.filterNot(_ == nestedClass).flatMap(liftInnerClassesWithSameName) ++ c.methodMembers ++ c.fieldMembers), nestedClass.copy( name = s"${nestedClass.name}$$${nestedClass.name}", static = false, @@ -100,7 +105,7 @@ trait Output { this: TransformCake => ) ) case _ => - Seq(c.copy(members = c.classMembers.flatMap(liftInnerClassesWithSameName) ++ c.methodMembers)) + Seq(c.copy(members = c.classMembers.flatMap(liftInnerClassesWithSameName) ++ c.methodMembers ++ c.fieldMembers)) } } @@ -124,7 +129,7 @@ trait Output { this: TransformCake => case (Some(o), Some(c)) => merge(o, c, forwarders, staticScope) case (Some(o), None) if forwarders => merge(o, fabricateCompanion(o), forwarders, staticScope) case (Some(o), None) => Vector(mangleModule(o, addMODULE = forwarders, pruneClasses = false)) - case (None, Some(c)) => Vector(c.copy(members = flattenObjects(c.classMembers) ++ c.methodMembers.sortBy(_.name))) + case (None, Some(c)) => Vector(c.copy(members = flattenObjects(c.classMembers) ++ c.methodMembers.sortBy(_.name) ++ c.fieldMembers.sortBy(_.name))) case (None, None) => ??? } } @@ -155,8 +160,8 @@ trait Output { this: TransformCake => else None val members = moduleInstance ++: ( - if (pruneClasses) obj.methodMembers - else flattenObjects(obj.classMembers) ++ obj.methodMembers + if (pruneClasses) obj.methodMembers ++ obj.fieldMembers + else flattenObjects(obj.classMembers) ++ obj.methodMembers ++ obj.fieldMembers ) val (com: Seq[String], moduleMembers: Vector[Templ]) = members.foldLeft((obj.comment, Vector.empty[Templ]))((p, mem) => mem match { @@ -174,6 +179,12 @@ trait Output { this: TransformCake => m.copy(comment = Seq("/**", " * Accessor for nested Scala object", " * @return (undocumented)", " */")) else m } + val fields = cls.members collect { + case f: FieldInfo => + if (f.ret.endsWith("$") && classes.exists(_.name == f.name)) + f.copy(comment = Seq("/**", " * Accessor for nested Scala object", " * @return (undocumented)", " */")) + else f + } val staticClasses = obj.members collect { case c: ClassInfo => c.copy( @@ -194,11 +205,11 @@ trait Output { this: TransformCake => val nestedClasses = flattenObjects(classes, forwarders = false, staticScope = false) val nestedStaticClasses = flattenObjects(staticClasses, forwarders = false, staticScope = staticScope) if (forwarders) { - val base = cls.copy(members = nestedClasses ++ nestedStaticClasses ++ staticMethods ++ methods) + val base = cls.copy(members = nestedClasses ++ nestedStaticClasses ++ staticMethods ++ methods ++ fields) val mod = mangleModule(obj, addMODULE = forwarders, pruneClasses = true) Vector(base, mod) } else { - val base = cls.copy(members = nestedClasses ++ staticMethods ++ methods) + val base = cls.copy(members = nestedClasses ++ staticMethods ++ methods ++ fields) val mod = mangleModule(obj, addMODULE = forwarders, pruneClasses = true) Vector(base, mod.copy(members = nestedStaticClasses ++ mod.members)) } diff --git a/plugin/src/main/scala/com/typesafe/genjavadoc/Plugin.scala b/plugin/src/main/scala/com/typesafe/genjavadoc/Plugin.scala index 37c7af6..7d2f4f4 100644 --- a/plugin/src/main/scala/com/typesafe/genjavadoc/Plugin.scala +++ b/plugin/src/main/scala/com/typesafe/genjavadoc/Plugin.scala @@ -50,6 +50,8 @@ class GenJavadocPlugin(val global: Global) extends Plugin { lazy val fabricateParams = java.lang.Boolean.parseBoolean(myOptions.getProperty("fabricateParams", "true")) lazy val strictVisibility = java.lang.Boolean.parseBoolean(myOptions.getProperty("strictVisibility", "false")) lazy val allowedAnnotations: Set[String] = stringToFilter(myOptions.getProperty("annotations", "")) + lazy val createfield = java.lang.Boolean.parseBoolean(myOptions.getProperty("createfields", "false")) + lazy val borrowConstructorArgsComment = java.lang.Boolean.parseBoolean(myOptions.getProperty("borrowConstructorArgsComment", "false")) private object MyComponent extends PluginComponent with Transform { @@ -73,6 +75,8 @@ class GenJavadocPlugin(val global: Global) extends Plugin { override val suppressSynthetic: Boolean = GenJavadocPlugin.this.suppressSynthetic override val fabricateParams: Boolean = GenJavadocPlugin.this.fabricateParams override val strictVisibility: Boolean = GenJavadocPlugin.this.strictVisibility + override val createField: Boolean = GenJavadocPlugin.this.createfield + override val borrowConstructorArgsComment: Boolean = GenJavadocPlugin.this.createfield && GenJavadocPlugin.this.borrowConstructorArgsComment override def superTransformUnit(unit: CompilationUnit) = super.transformUnit(unit) override def superTransform(tree: Tree) = super.transform(tree)