From d07ce7cae87da8f1c28c261f3b17ad20d6511ef1 Mon Sep 17 00:00:00 2001 From: love2hina Date: Sun, 13 Feb 2022 17:25:20 +0900 Subject: [PATCH] =?UTF-8?q?Enum=E5=80=A4=E3=81=AE=E5=87=BA=E5=8A=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit refs #19 --- build.gradle.kts | 2 +- .../kotlin/sharon/SmartXMLStreamWriter.kt | 11 +- .../net/love2hina/kotlin/sharon/Utility.kt | 34 ----- .../love2hina/kotlin/sharon/data/ClassInfo.kt | 8 +- .../kotlin/sharon/data/EnumEntryInfo.kt | 35 +++++ .../love2hina/kotlin/sharon/data/EnumInfo.kt | 44 ++++++ .../kotlin/sharon/data/JavadocInfo.kt | 60 ++++++++ .../kotlin/sharon/data/MethodInfo.kt | 19 +-- .../kotlin/sharon/parser/ClassVisitor.kt | 23 +-- .../kotlin/sharon/parser/ConditionVisitor.kt | 6 +- .../kotlin/sharon/parser/EnumVisitor.kt | 142 ++++++++++++++++++ .../kotlin/sharon/parser/FunctionVisitor.kt | 23 +-- .../love2hina/kotlin/sharon/parser/Parser.kt | 128 +++------------- src/main/powershell/DocumentGenerator.ps1 | 27 +++- src/main/powershell/TargetInfo.psm1 | 114 ++++++++++++++ ...tTarget.java => ParseTestClassTarget.java} | 8 +- .../kotlin/sharon/ParseTestEnumTarget.java | 24 +++ template/test.xlsx | Bin 24570 -> 28524 bytes 18 files changed, 482 insertions(+), 226 deletions(-) create mode 100644 src/main/kotlin/net/love2hina/kotlin/sharon/data/EnumEntryInfo.kt create mode 100644 src/main/kotlin/net/love2hina/kotlin/sharon/data/EnumInfo.kt create mode 100644 src/main/kotlin/net/love2hina/kotlin/sharon/parser/EnumVisitor.kt rename src/test/java/net/love2hina/kotlin/sharon/{ParseTestTarget.java => ParseTestClassTarget.java} (89%) create mode 100644 src/test/java/net/love2hina/kotlin/sharon/ParseTestEnumTarget.java diff --git a/build.gradle.kts b/build.gradle.kts index f4f6213..0c689de 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -35,7 +35,7 @@ plugins { } group = "net.love2hina" -version = "0.2.0-alpha1" +version = "0.3.0-SNAPSHOT" val versions: Map by extra diff --git a/src/main/kotlin/net/love2hina/kotlin/sharon/SmartXMLStreamWriter.kt b/src/main/kotlin/net/love2hina/kotlin/sharon/SmartXMLStreamWriter.kt index ff13a94..7415609 100644 --- a/src/main/kotlin/net/love2hina/kotlin/sharon/SmartXMLStreamWriter.kt +++ b/src/main/kotlin/net/love2hina/kotlin/sharon/SmartXMLStreamWriter.kt @@ -34,8 +34,7 @@ internal class SmartXMLStreamWriter(file: File): XMLStreamWriter, Closeable { private fun outputIndent() { xmlWriter.writeCharacters(lineSeparator) - if (level > 0) - xmlWriter.writeCharacters(" ".repeat(indentSize * level)) + if (level > 0) { xmlWriter.writeCharacters(" ".repeat(indentSize * level)) } } private fun outputElementBody() { @@ -139,15 +138,15 @@ internal class SmartXMLStreamWriter(file: File): XMLStreamWriter, Closeable { } override fun writeAttribute(localName: String, value: String?) { - if (value != null) xmlWriter.writeAttribute(localName, value) + if (value != null) { xmlWriter.writeAttribute(localName, value) } } override fun writeAttribute(namespaceURI: String?, localName: String, value: String?) { - if (value != null) xmlWriter.writeAttribute(namespaceURI, localName, value) + if (value != null) { xmlWriter.writeAttribute(namespaceURI, localName, value) } } override fun writeAttribute(prefix: String?, namespaceURI: String?, localName: String, value: String?) { - if (value != null) xmlWriter.writeAttribute(prefix, namespaceURI, localName, value) + if (value != null) { xmlWriter.writeAttribute(prefix, namespaceURI, localName, value) } } override fun writeComment(data: String?) { @@ -179,7 +178,7 @@ internal class SmartXMLStreamWriter(file: File): XMLStreamWriter, Closeable { else -> { // 複数行出力 lines.stream() - .forEach{ + .forEach { outputElementBody() xmlWriter.writeCharacters(it.trim()) } diff --git a/src/main/kotlin/net/love2hina/kotlin/sharon/Utility.kt b/src/main/kotlin/net/love2hina/kotlin/sharon/Utility.kt index be71504..2c517c4 100644 --- a/src/main/kotlin/net/love2hina/kotlin/sharon/Utility.kt +++ b/src/main/kotlin/net/love2hina/kotlin/sharon/Utility.kt @@ -2,8 +2,6 @@ package net.love2hina.kotlin.sharon import com.github.javaparser.ast.Modifier import com.github.javaparser.ast.NodeList -import com.github.javaparser.ast.comments.Comment -import net.love2hina.kotlin.sharon.data.* import kotlin.reflect.KClass import kotlin.reflect.KMutableProperty @@ -51,35 +49,3 @@ internal fun getModifier(modifiers: NodeList): String = .reduce(StringBuilder(), { a: StringBuilder, i: String -> a.appendSeparator(COMMA, i) }, { a: StringBuilder, b: StringBuilder -> a.appendSeparator(COMMA, b) }).toString() - -internal fun getCommentContents(comment: Comment): String = - comment.content.split(REGEXP_NEWLINE) - .stream().map { l -> - val m = REGEXP_BLOCK_COMMENT.find(l) - if (m != null) - (m.groups as MatchNamedGroupCollection)["content"]?.value ?: "" - else l - } - .reduce(StringBuilder(), - { b, l -> b.appendNewLine(l) }, - { b1, b2 -> b1.appendNewLine(b2) }) - .toString() - -internal fun pushJavadocComment(content: String, name: String?, start: Int, range: IntRange, info: JavadocInfo ): Int { - - if (start < range.start) { - val value = content.substring(start, range.start).trim() - - when { - info.parseTag(name, value) -> {} - name == null -> { - info.description.appendNewLine(value) - } - else -> { - info.description.appendNewLine("@$name $value") - } - } - } - - return range.endInclusive + 1 -} diff --git a/src/main/kotlin/net/love2hina/kotlin/sharon/data/ClassInfo.kt b/src/main/kotlin/net/love2hina/kotlin/sharon/data/ClassInfo.kt index e98e9fb..82fc027 100644 --- a/src/main/kotlin/net/love2hina/kotlin/sharon/data/ClassInfo.kt +++ b/src/main/kotlin/net/love2hina/kotlin/sharon/data/ClassInfo.kt @@ -27,15 +27,12 @@ internal data class ClassInfo( /* * @see - * {@link} - * {@linkplain} - * {@docRoot} */ ): JavadocInfo { - override fun parseTag(name: String?, value: String): Boolean { - return when (name) { + override fun parseTag(name: String?, value: String): Boolean + = when (name) { "param" -> { val r = Regex("^<(?\\w+)>(?:\\s+(?.*))?$") val m = r.find(value) @@ -65,6 +62,5 @@ internal data class ClassInfo( } else -> false } - } } diff --git a/src/main/kotlin/net/love2hina/kotlin/sharon/data/EnumEntryInfo.kt b/src/main/kotlin/net/love2hina/kotlin/sharon/data/EnumEntryInfo.kt new file mode 100644 index 0000000..7b74bc6 --- /dev/null +++ b/src/main/kotlin/net/love2hina/kotlin/sharon/data/EnumEntryInfo.kt @@ -0,0 +1,35 @@ +package net.love2hina.kotlin.sharon.data + +import net.love2hina.kotlin.sharon.setProperty + +internal data class EnumEntryInfo( + + /** 説明 */ + override val description: StringBuilder = StringBuilder(), + + /** since */ + var since: String? = null, + + /** deprecated */ + var deprecated: String? = null, + + /** serial */ + var serial: String? = null, + + /* + * @see + * @serialField + */ + +): JavadocInfo { + + override fun parseTag(name: String?, value: String): Boolean + = when (name) { + "since", "deprecated", "serial" -> { + this.setProperty(name, value) + true + } + else -> false + } + +} diff --git a/src/main/kotlin/net/love2hina/kotlin/sharon/data/EnumInfo.kt b/src/main/kotlin/net/love2hina/kotlin/sharon/data/EnumInfo.kt new file mode 100644 index 0000000..a1e4726 --- /dev/null +++ b/src/main/kotlin/net/love2hina/kotlin/sharon/data/EnumInfo.kt @@ -0,0 +1,44 @@ +package net.love2hina.kotlin.sharon.data + +import net.love2hina.kotlin.sharon.setProperty + +internal data class EnumInfo( + + /** 説明 */ + override val description: StringBuilder = StringBuilder(), + + /** since */ + var since: String? = null, + + /** deprecated */ + var deprecated: String? = null, + + /** serial */ + var serial: String? = null, + + /** author */ + val author: MutableList = mutableListOf(), + + /** version */ + var version: String? = null, + + /* + * @see + */ + +): JavadocInfo { + + override fun parseTag(name: String?, value: String): Boolean + = when (name) { + "author" -> { + author.addAll(value.split(", ")) + true + } + "since", "deprecated", "serial", "version" -> { + this.setProperty(name, value) + true + } + else -> false + } + +} diff --git a/src/main/kotlin/net/love2hina/kotlin/sharon/data/JavadocInfo.kt b/src/main/kotlin/net/love2hina/kotlin/sharon/data/JavadocInfo.kt index 3d58e38..412ccc8 100644 --- a/src/main/kotlin/net/love2hina/kotlin/sharon/data/JavadocInfo.kt +++ b/src/main/kotlin/net/love2hina/kotlin/sharon/data/JavadocInfo.kt @@ -1,5 +1,9 @@ package net.love2hina.kotlin.sharon.data +import com.github.javaparser.ast.comments.Comment +import net.love2hina.kotlin.sharon.appendNewLine +import java.util.Optional + internal interface JavadocInfo { val description: StringBuilder @@ -7,3 +11,59 @@ internal interface JavadocInfo { fun parseTag(name: String?, value: String): Boolean } + +internal fun Optional.parseJavadoc(info: JavadocInfo) { + this.ifPresent { + if (it.isJavadocComment) { + val content = getCommentContents(it) + var index = 0 + var name: String? = null + + // Javadocをパースする + for (match in REGEXP_ANNOTATION.findAll(content)) { + index = pushJavadocComment(content, name, index, match.range, info) + name = (match.groups as MatchNamedGroupCollection)["name"]!!.value + } + pushJavadocComment(content, name, index, IntRange(content.length, content.length), info) + } + else if (it.isBlockComment) { + val content = getCommentContents(it) + info.description.appendNewLine(content) + } + else { + info.description.appendNewLine(it.content) + } + } +} + +private fun getCommentContents(comment: Comment): String = + comment.content.split(REGEXP_NEWLINE) + .stream().map { l -> + val m = REGEXP_BLOCK_COMMENT.find(l) + if (m != null) + (m.groups as MatchNamedGroupCollection)["content"]?.value ?: "" + else l + } + .reduce(StringBuilder(), + { b, l -> b.appendNewLine(l) }, + { b1, b2 -> b1.appendNewLine(b2) }) + .toString() + +private fun pushJavadocComment(content: String, name: String?, start: Int, range: IntRange, info: JavadocInfo ): Int { + + if (start < range.start) { + val value = content.substring(start, range.start).trim() + + when { + info.parseTag(name, value) -> {} + name == null -> { + info.description.appendNewLine(value) + } + else -> { + info.description.appendNewLine("@$name $value") + } + } + } + + return range.endInclusive + 1 +} diff --git a/src/main/kotlin/net/love2hina/kotlin/sharon/data/MethodInfo.kt b/src/main/kotlin/net/love2hina/kotlin/sharon/data/MethodInfo.kt index a6f1551..01d98b7 100644 --- a/src/main/kotlin/net/love2hina/kotlin/sharon/data/MethodInfo.kt +++ b/src/main/kotlin/net/love2hina/kotlin/sharon/data/MethodInfo.kt @@ -20,20 +20,16 @@ internal data class MethodInfo( val throws: HashMap = LinkedHashMap(), /* - * see -@since -@deprecated -@serialData -{@link} -{@linkplain} -{@inheritDoc} -{@docRoot} - * */ + * see + * @since + * @deprecated + * @serialData + */ ): JavadocInfo { - override fun parseTag(name: String?, value: String): Boolean { - return when (name) { + override fun parseTag(name: String?, value: String): Boolean + = when (name) { "param" -> { val r = Regex("^(?:(?\\w+)|<(?\\w+)>)(?:\\s+(?.*))?$") val m = r.find(value) @@ -86,6 +82,5 @@ internal data class MethodInfo( } else -> false } - } } diff --git a/src/main/kotlin/net/love2hina/kotlin/sharon/parser/ClassVisitor.kt b/src/main/kotlin/net/love2hina/kotlin/sharon/parser/ClassVisitor.kt index f15d0c4..5b6189d 100644 --- a/src/main/kotlin/net/love2hina/kotlin/sharon/parser/ClassVisitor.kt +++ b/src/main/kotlin/net/love2hina/kotlin/sharon/parser/ClassVisitor.kt @@ -23,27 +23,7 @@ internal fun Parser.Visitor.visitInClass(n: ClassOrInterfaceDeclaration, arg: Vo } // コメントの解析 - n.comment.ifPresent { - if (it.isJavadocComment) { - val content = getCommentContents(it) - var index = 0 - var name: String? = null - - // Javadocをパースする - for (match in REGEXP_ANNOTATION.findAll(content)) { - index = pushJavadocComment(content, name, index, match.range, classInfo) - name = (match.groups as MatchNamedGroupCollection)["name"]!!.value - } - pushJavadocComment(content, name, index, IntRange(content.length, content.length), classInfo) - } - else if (it.isBlockComment) { - val content = getCommentContents(it) - classInfo.description.appendNewLine(content) - } - else { - classInfo.description.appendNewLine(it.content) - } - } + n.comment.parseJavadoc(classInfo) // 出力開始 writer.writeStartElement("class") @@ -90,7 +70,6 @@ internal fun Parser.Visitor.visitInClass(n: ClassOrInterfaceDeclaration, arg: Vo // アノテーション n.annotations.forEach { it.accept(this, arg) } - // メンバー n.members.forEach { it.accept(this, arg) } diff --git a/src/main/kotlin/net/love2hina/kotlin/sharon/parser/ConditionVisitor.kt b/src/main/kotlin/net/love2hina/kotlin/sharon/parser/ConditionVisitor.kt index b04171c..47dfda4 100644 --- a/src/main/kotlin/net/love2hina/kotlin/sharon/parser/ConditionVisitor.kt +++ b/src/main/kotlin/net/love2hina/kotlin/sharon/parser/ConditionVisitor.kt @@ -93,8 +93,7 @@ internal fun Parser.Visitor.visitInSwitch(n: SwitchStmt, arg: Void?) { // 条件エントリ for (i in n.entries) { - if (!caseContinue) - writer.writeStartElement("case") + if (!caseContinue) { writer.writeStartElement("case") } // コメント i.comment.ifPresent { it.accept(this, arg) } @@ -110,8 +109,7 @@ internal fun Parser.Visitor.visitInSwitch(n: SwitchStmt, arg: Void?) { writer.writeEndElement() caseContinue = i.statements.isEmpty() - if (!caseContinue) - writer.writeEndElement() + if (!caseContinue) { writer.writeEndElement() } } writer.writeEndElement() diff --git a/src/main/kotlin/net/love2hina/kotlin/sharon/parser/EnumVisitor.kt b/src/main/kotlin/net/love2hina/kotlin/sharon/parser/EnumVisitor.kt new file mode 100644 index 0000000..c5301fd --- /dev/null +++ b/src/main/kotlin/net/love2hina/kotlin/sharon/parser/EnumVisitor.kt @@ -0,0 +1,142 @@ +package net.love2hina.kotlin.sharon.parser + +import com.github.javaparser.ast.body.EnumConstantDeclaration +import com.github.javaparser.ast.body.EnumDeclaration +import com.github.javaparser.ast.expr.* +import net.love2hina.kotlin.sharon.data.* +import net.love2hina.kotlin.sharon.* + +/** + * Enum定義. + * + * `enum enum_name` + */ +internal fun Parser.Visitor.visitInEnum(n: EnumDeclaration, arg: Void?) { + val enumName = n.name.asString() + + // Enum情報 + val enumInfo = EnumInfo() + + // 実体解析 + // コメントの解析 + n.comment.parseJavadoc(enumInfo) + + // 出力開始 + writer.writeStartElement("enum") + writer.writeAttribute("modifier", getModifier(n.modifiers)) + writer.writeAttribute("name", enumName) + writer.writeAttribute("fullname", packageStack.getFullName(enumName)) + + packageStack.push(enumName) + + // インターフェース + n.implementedTypes.forEach { + writer.writeEmptyElement("implements") + writer.writeAttribute("name", it.name.asString()) + } + + // Javadoc + writer.writeStartElement("javadoc") + writer.writeAttribute("since", enumInfo.since) + writer.writeAttribute("deprecated", enumInfo.deprecated) + writer.writeAttribute("serial", enumInfo.serial) + writer.writeAttribute("version", enumInfo.version) + enumInfo.author.forEach { + writer.writeStartElement("author") + writer.writeStrings(it) + writer.writeEndElement() + } + writer.writeEndElement() + // 説明の出力 + writer.writeStartElement("description") + writer.writeStrings(enumInfo.description.toString()) + writer.writeEndElement() + + // アノテーション + n.annotations.forEach { it.accept(this, arg) } + // エントリー + n.entries.forEach { it.accept(this, arg) } + // メンバー + n.members.forEach { it.accept(this, arg) } + + packageStack.pop() + writer.writeEndElement() +} + +/** + * Enum値定義. + */ +internal fun Parser.Visitor.visitInEnumConstant(n: EnumConstantDeclaration, arg: Void?) { + val valueName = n.name.asString() + + // エントリ情報 + val entryInfo = EnumEntryInfo() + + // 実体解析 + // コメントの解析 + n.comment.parseJavadoc(entryInfo) + + // TODO + // n.getClassBody().forEach(p -> p.accept(this, arg)); + + // 出力開始 + writer.writeStartElement("entry") + writer.writeAttribute("name", valueName) + + // Javadoc + writer.writeStartElement("javadoc") + writer.writeAttribute("since", entryInfo.since) + writer.writeAttribute("deprecated", entryInfo.deprecated) + writer.writeAttribute("serial", entryInfo.serial) + writer.writeEndElement() + // 説明の出力 + writer.writeStartElement("description") + writer.writeStrings(entryInfo.description.toString()) + writer.writeEndElement() + + // アノテーション + n.annotations.forEach { it.accept(this, arg) } + // パラメーター + n.arguments.forEach { + writer.writeStartElement("argument") + when (it) { + is NullLiteralExpr -> { + writer.writeAttribute("type", "null") + } + is BooleanLiteralExpr -> { + writer.writeAttribute("type", "boolean") + writer.writeStrings(it.value.toString()) + } + is CharLiteralExpr -> { + writer.writeAttribute("type", "char") + writer.writeStrings(it.value) + } + is IntegerLiteralExpr -> { + writer.writeAttribute("type", "integer") + writer.writeStrings(it.value) + } + is LongLiteralExpr -> { + writer.writeAttribute("type", "long") + writer.writeStrings(it.value) + } + is DoubleLiteralExpr -> { + writer.writeAttribute("type", "double") + writer.writeStrings(it.value) + } + is StringLiteralExpr -> { + writer.writeAttribute("type", "string") + writer.writeStrings(it.value) + } + is TextBlockLiteralExpr -> { + writer.writeAttribute("type", "textblock") + writer.writeStrings(it.value) + } + else -> { + writer.writeAttribute("type", "composite") + } + } + writer.writeEndElement() + } + + writer.writeEndElement() +} diff --git a/src/main/kotlin/net/love2hina/kotlin/sharon/parser/FunctionVisitor.kt b/src/main/kotlin/net/love2hina/kotlin/sharon/parser/FunctionVisitor.kt index 1fd577a..0feb916 100644 --- a/src/main/kotlin/net/love2hina/kotlin/sharon/parser/FunctionVisitor.kt +++ b/src/main/kotlin/net/love2hina/kotlin/sharon/parser/FunctionVisitor.kt @@ -42,28 +42,7 @@ internal fun Parser.Visitor.visitInFunction(n: MethodDeclaration, arg: Void?) { } // コメントの解析 - n.comment.ifPresent { - if (it.isJavadocComment) { - // Javadocコメントから、*を取り除く - val content = getCommentContents(it) - var index = 0 - var name: String? = null - - // Javadocをパースする - for (match in REGEXP_ANNOTATION.findAll(content)) { - index = pushJavadocComment(content, name, index, match.range, methodInfo) - name = (match.groups as MatchNamedGroupCollection)["name"]!!.value - } - pushJavadocComment(content, name, index, IntRange(content.length, content.length), methodInfo) - } - else if (it.isBlockComment) { - val content = getCommentContents(it) - methodInfo.description.appendNewLine(content) - } - else { - methodInfo.description.appendNewLine(it.content) - } - } + n.comment.parseJavadoc(methodInfo) // 出力開始 writer.writeStartElement("method") diff --git a/src/main/kotlin/net/love2hina/kotlin/sharon/parser/Parser.kt b/src/main/kotlin/net/love2hina/kotlin/sharon/parser/Parser.kt index 458cbb0..839151e 100644 --- a/src/main/kotlin/net/love2hina/kotlin/sharon/parser/Parser.kt +++ b/src/main/kotlin/net/love2hina/kotlin/sharon/parser/Parser.kt @@ -179,40 +179,8 @@ internal class Parser( // n.comment.ifPresent { it.accept(this, arg) } } - /** - * Enum定義. - * - * `enum enum_name` - */ - override fun visit(n: EnumDeclaration?, arg: Void?) { - val name = n!!.name.asString() - - writer.writeStartElement("enum") - writer.writeAttribute("modifier", getModifier(n.modifiers)) - writer.writeAttribute("name", name) - writer.writeAttribute("fullname", packageStack.getFullName(name)) - - // TODO - // n.entries.forEach { it.accept(this, arg) } - // n.getImplementedTypes().forEach(p -> p.accept(this, arg)); - // n.getMembers().forEach(p -> p.accept(this, arg)); - // n.getAnnotations().forEach(p -> p.accept(this, arg)); - // n.getComment().ifPresent(l -> l.accept(this, arg)); - - writer.writeEndElement() - } - - /** - * Enum値定義. - */ - override fun visit(n: EnumConstantDeclaration?, arg: Void?) { - // TODO - // n.getArguments().forEach(p -> p.accept(this, arg)); - // n.getClassBody().forEach(p -> p.accept(this, arg)); - // n.getName().accept(this, arg); - // n.getAnnotations().forEach(p -> p.accept(this, arg)); - // n.getComment().ifPresent(l -> l.accept(this, arg)); - } + override fun visit(n: EnumDeclaration?, arg: Void?) = visitInEnum(n!!, arg) + override fun visit(n: EnumConstantDeclaration?, arg: Void?) = visitInEnumConstant(n!!, arg) /** * アノテーション定義. @@ -421,8 +389,8 @@ internal class Parser( // コメント n.comment.ifPresent { it.accept(this, arg) } - // 式 - n.expression.accept(this, arg) + // TODO: 式 + // n.expression.accept(this, arg) } /** @@ -453,80 +421,18 @@ internal class Parser( n!!.comment.ifPresent { it.accept(this, arg) } } - /** - * nullリテラル値. - */ - override fun visit(n: NullLiteralExpr?, arg: Void?) { - // コメント - n!!.comment.ifPresent { it.accept(this, arg) } - } - - /** - * Booleanリテラル値. - */ - override fun visit(n: BooleanLiteralExpr?, arg: Void?) { - // コメント - n!!.comment.ifPresent { it.accept(this, arg) } - } - - /** - * Charリテラル値. - */ - override fun visit(n: CharLiteralExpr?, arg: Void?) { - // コメント - n!!.comment.ifPresent { it.accept(this, arg) } - } - - /** - * Integerリテラル値. - */ - override fun visit(n: IntegerLiteralExpr?, arg: Void?) { - // コメント - n!!.comment.ifPresent { it.accept(this, arg) } - } - - /** - * Longリテラル値. - */ - override fun visit(n: LongLiteralExpr?, arg: Void?) { - // コメント - n!!.comment.ifPresent { it.accept(this, arg) } - } - - /** - * Doubleリテラル値. - */ - override fun visit(n: DoubleLiteralExpr?, arg: Void?) { - // コメント - n!!.comment.ifPresent { it.accept(this, arg) } - } - - /** - * Stringリテラル値. - */ - override fun visit(n: StringLiteralExpr?, arg: Void?) { - // コメント - n!!.comment.ifPresent { it.accept(this, arg) } - } - - /** - * TextBlockリテラル値. - */ - override fun visit(n: TextBlockLiteralExpr?, arg: Void?) { - // コメント - n!!.comment.ifPresent { it.accept(this, arg) } - } + override fun visit(n: NullLiteralExpr?, arg: Void?) = error("使われない") + override fun visit(n: BooleanLiteralExpr?, arg: Void?) = error("使われない") + override fun visit(n: CharLiteralExpr?, arg: Void?) = error("使われない") + override fun visit(n: IntegerLiteralExpr?, arg: Void?) = error("使われない") + override fun visit(n: LongLiteralExpr?, arg: Void?) = error("使われない") + override fun visit(n: DoubleLiteralExpr?, arg: Void?) = error("使われない") + override fun visit(n: StringLiteralExpr?, arg: Void?) = error("使われない") + override fun visit(n: TextBlockLiteralExpr?, arg: Void?) = error("使われない") override fun visit(n: IfStmt?, arg: Void?) = visitInIf(n!!, arg) override fun visit(n: SwitchStmt?, arg: Void?) = visitInSwitch(n!!, arg) - - /** - * caseステートメント. - */ - override fun visit(n: SwitchEntry?, arg: Void?) { - // 使われない - throw IllegalAccessException() - } + override fun visit(n: SwitchEntry?, arg: Void?) = error("使われない") override fun visit(n: ForStmt?, arg: Void?) = visitInFor(n!!, arg) override fun visit(n: ForEachStmt?, arg: Void?) = visitInForEach(n!!, arg) @@ -575,8 +481,8 @@ internal class Parser( // コメント n.comment.ifPresent { it.accept(this, arg) } - // 式 - n.expression.accept(this, arg) + // TODO: 式 + // n.expression.accept(this, arg) } /** @@ -587,8 +493,8 @@ internal class Parser( // コメント n.comment.ifPresent { it.accept(this, arg) } - // 式 - n.expression.ifPresent { it.accept(this, arg) } + // TODO: 式 + // n.expression.ifPresent { it.accept(this, arg) } } override fun visit(n: NodeList<*>?, arg: Void?) { diff --git a/src/main/powershell/DocumentGenerator.ps1 b/src/main/powershell/DocumentGenerator.ps1 index 21c5a66..d7f6c25 100644 --- a/src/main/powershell/DocumentGenerator.ps1 +++ b/src/main/powershell/DocumentGenerator.ps1 @@ -50,7 +50,13 @@ class DocumentGenerator: System.IDisposable { hidden [XPathNavigator] $xpath DocumentGenerator() { - $this.excel.Visible = $true + if ($global:DebugPreference -ne 'SilentlyContinue') { + # デバッグが有効なら表示する + $this.excel.Visible = $true + } + + # 警告表示を無効化 + $this.excel.DisplayAlerts = $false # テンプレートを開く $this.bookTemplate = $this.excel.Workbooks.Open($script:FileTemplate, 0, $true) @@ -82,8 +88,14 @@ class DocumentGenerator: System.IDisposable { Remove-Item -Path $script:FileDocument } - # 保存する - $this.bookDocument.SaveAs($script:FileDocument) + if ($this.bookDocument.WorkSheets.Count -gt 1) { + # 先頭シートを削除する + $this.bookDocument.WorkSheets.Item(1).Delete() + + # 保存する + $this.bookDocument.SaveAs($script:FileDocument) + } + $this.bookDocument.Close($false) } @@ -171,7 +183,7 @@ class DocumentGenerator: System.IDisposable { # 親シートはなし $parSheetFmt = $null } - 'type|class' { + 'type|class|enum' { # テンプレートルートに追加 $this.format.entries += $curSheetFmt # 親シートを設定 @@ -235,6 +247,13 @@ class DocumentGenerator: System.IDisposable { return [ClassTargetInfo]::new($node) }) } + 'enum' { + # 列挙型情報 + $targetCursor = [RootTargetEnumerable]::new($parent, 'enum', [Func[XPathNavigator, TargetInfo]]{ + param([XPathNavigator] $node) + return [EnumTargetInfo]::new($node) + }) + } 'method' { # メソッド $targetCursor = [RootTargetEnumerable]::new($parent, 'method', [Func[XPathNavigator, TargetInfo]]{ diff --git a/src/main/powershell/TargetInfo.psm1 b/src/main/powershell/TargetInfo.psm1 index f888e8f..7583816 100644 --- a/src/main/powershell/TargetInfo.psm1 +++ b/src/main/powershell/TargetInfo.psm1 @@ -123,6 +123,120 @@ class ClassTargetInfo : TargetInfo { } +class EnumTargetInfo : TargetInfo { + + # 修飾子 + [string] $modifier + # 名前 + [string] $name + # 完全名 + [string] $fullname + # インターフェース + [string[]] $implements + + # since(Javadoc) + [string] $since + # deprecated(Javadoc) + [string] $deprecated + # serial(Javadoc) + [string] $serial + # version(Javadoc) + [string] $version + # author(Javadoc) + [string[]] $author + + # 説明 + [string] $description + + # エントリー + [TargetEnumerable] $entries + # フィールド + [TargetEnumerable] $fields + # メソッド + [TargetEnumerable] $methods + + EnumTargetInfo([XPathNavigator] $node) : base($node) { + + $this.modifier = $node.Evaluate('@modifier') + $this.name = $node.Evaluate('@name') + $this.fullname = $node.Evaluate('@fullname') + + $this.implements = [TargetInfo]::EvaluateStringArray($node, 'implements/@name') + + $this.since = $node.Evaluate('javadoc/@since') + $this.deprecated = $node.Evaluate('javadoc/@deprecated') + $this.serial = $node.Evaluate('javadoc/@serial') + $this.version = $node.Evaluate('javadoc/@version') + + $this.author = [TargetInfo]::EvaluateStringArray($node, 'javadoc/author/text()') + + $this.description = $node.Evaluate('description/text()') + + $this.entries = [TargetEnumerable]::new($node, 'entry', [Func[XPathNavigator, TargetInfo]]{ + param([XPathNavigator] $_node) + return [EnumEntryTargetInfo]::new($_node) + }) + $this.fields = [TargetEnumerable]::new($node, 'field', [Func[XPathNavigator, TargetInfo]]{ + param([XPathNavigator] $_node) + return [FieldTargetInfo]::new($_node) + }) + $this.methods = [TargetEnumerable]::new($node, 'method', [Func[XPathNavigator, TargetInfo]]{ + param([XPathNavigator] $_node) + return [MethodTargetInfo]::new($_node) + }) + } + +} + +class EnumEntryTargetInfo : TargetInfo { + + # 名前 + [string] $name + + # since(Javadoc) + [string] $since + # deprecated(Javadoc) + [string] $deprecated + # serial(Javadoc) + [string] $serial + + # 説明 + [string] $description + + # 引数 + [TargetEnumerable] $arguments + + EnumEntryTargetInfo([XPathNavigator] $node) : base($node) { + + $this.name = $node.Evaluate('@name') + + $this.since = $node.Evaluate('javadoc/@since') + $this.deprecated = $node.Evaluate('javadoc/@deprecated') + $this.serial = $node.Evaluate('javadoc/@serial') + + $this.description = $node.Evaluate('description/text()') + + $this.arguments = [TargetEnumerable]::new($node, 'argument', [Func[XPathNavigator, TargetInfo]]{ + param([XPathNavigator] $_node) + return [ArgumentTargetInfo]::new($_node) + }) + } +} + +class ArgumentTargetInfo : TargetInfo { + + # 型 + [string] $type + # 値 + [string] $value + + ArgumentTargetInfo([XPathNavigator] $node) : base($node) { + + $this.type = $node.Evaluate('@type') + $this.value = $node.Evaluate('text()') + } +} + class FieldTargetInfo : TargetInfo { # コメント diff --git a/src/test/java/net/love2hina/kotlin/sharon/ParseTestTarget.java b/src/test/java/net/love2hina/kotlin/sharon/ParseTestClassTarget.java similarity index 89% rename from src/test/java/net/love2hina/kotlin/sharon/ParseTestTarget.java rename to src/test/java/net/love2hina/kotlin/sharon/ParseTestClassTarget.java index 8c903c5..d30886d 100644 --- a/src/test/java/net/love2hina/kotlin/sharon/ParseTestTarget.java +++ b/src/test/java/net/love2hina/kotlin/sharon/ParseTestClassTarget.java @@ -12,10 +12,10 @@ *

* * @author webmaster@love2hina.net - * @since 2022/02/09 - * @version 1.0.0 + * @since 0.1.0 + * @version 0.2.0 */ -public class ParseTestTarget extends Object implements java.io.Serializable { +public class ParseTestClassTarget extends Object implements java.io.Serializable { /** フィールド */ private int fieldInt = 0, fieldInt2 = 2; @@ -39,7 +39,7 @@ public enum MyEnum { * @throws Exception 例外 */ @MyAnnotation - public int method(ParseTestTarget this, final String name, Ty amount) throws Exception { + public int method(ParseTestClassTarget this, final String name, Ty amount) throws Exception { /// # 出力 System.out.println("Hello, world!"); diff --git a/src/test/java/net/love2hina/kotlin/sharon/ParseTestEnumTarget.java b/src/test/java/net/love2hina/kotlin/sharon/ParseTestEnumTarget.java new file mode 100644 index 0000000..9c53c94 --- /dev/null +++ b/src/test/java/net/love2hina/kotlin/sharon/ParseTestEnumTarget.java @@ -0,0 +1,24 @@ +package net.love2hina.kotlin.sharon; + +/** + * テスト列挙型. + * + * @author webmaster@love2hina.net + * @since 0.2.0 + * @version 0.2.0 + */ +public enum ParseTestEnumTarget { + /** 通常定義 */ + NORMAL, + /** コンストラクタ定義 */ + CONSTRUCTOR("value"); + + public final String value; + + ParseTestEnumTarget() { + value = null; + } + ParseTestEnumTarget(String v) { + value = v; + } +} diff --git a/template/test.xlsx b/template/test.xlsx index 8ee29493f165636712c336097e1851de0b5ac983..0cf3c39c3fc259355261af7ec305759c00db3103 100644 GIT binary patch delta 15642 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 delta 12828 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