diff --git a/.gitattributes b/.gitattributes index 66cc64a..1a33558 100644 --- a/.gitattributes +++ b/.gitattributes @@ -16,4 +16,5 @@ *.war binary # Custom +src/functionalTest/resources/scenarios/**/* text eol=lf src/functionalTest/resources/scenarios/base_java/src/main/java/test/TestClassCrlf*.java text eol=crlf diff --git a/build.gradle.kts b/build.gradle.kts index 33cff50..dc4ced5 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,12 +1,15 @@ plugins { - id("dev.yumi.gradle.licenser").version("1.1.+") - id("com.gradle.plugin-publish").version("1.2.0") - id("maven-publish") - id("signing") + id("dev.yumi.gradle.licenser") version "1.1.+" + id("com.gradle.plugin-publish") version "1.2.0" + + kotlin("jvm") version "1.9.24" + + `maven-publish` + signing } group = "dev.yumi" -version = "1.1.2" +version = "1.1.3" val javaVersion = 17 repositories { @@ -33,7 +36,8 @@ gradlePlugin { create("yumi_gradle_licenser") { id = "dev.yumi.gradle.licenser" displayName = "Yumi Gradle Licenser" - description = "A plugin to automatically manage license headers in project files, designed to be flexible to easily support many use cases like having different header kinds for different files." + description = + "A plugin to automatically manage license headers in project files, designed to be flexible to easily support many use cases like having different header kinds for different files." tags = listOf("licenser", "licensing", "licenses", "license-header") implementationClass = "dev.yumi.gradle.licenser.YumiLicenserGradlePlugin" } @@ -53,6 +57,11 @@ java { testResultsDir.set(layout.buildDirectory.dir("junit-xml")) } +kotlin { + // Require explicit visibility/type definitions for public types, among other things + explicitApi() +} + tasks.withType().configureEach { options.encoding = "UTF-8" options.isDeprecation = true diff --git a/src/main/java/dev/yumi/gradle/licenser/api/comment/HeaderCommentManager.java b/src/main/java/dev/yumi/gradle/licenser/api/comment/HeaderCommentManager.java index 79ff161..b5d7936 100644 --- a/src/main/java/dev/yumi/gradle/licenser/api/comment/HeaderCommentManager.java +++ b/src/main/java/dev/yumi/gradle/licenser/api/comment/HeaderCommentManager.java @@ -29,25 +29,65 @@ public class HeaderCommentManager { public HeaderCommentManager() { this.register(new PatternSet() .include( + // C/++ "**/*.c", "**/*.cpp", "**/*.cxx", "**/*.h", "**/*.hpp", "**/*.hxx", + + // Java "**/*.java", + + // Kotlin "**/*.kt", "**/*.kts", - "**/*.scala" + + // Scala + "**/*.scala", + + // Web languages + "**/*.dart", // Dart language + "**/*.js", // JavaScript + "**/*.jsx", // JavaScript XML + "**/*.ts", // TypeScript + "**/*.tsx", // TypeScript XML + + // Stylesheets + "**/*.css", // CSS stylesheets + "**/*.less", // Less (Extended CSS) + "**/*.scss", // SCSS (CSS syntax for SASS) + "**/*.styl" // Stylus (Alternative CSS syntax) ), CStyleHeaderComment.INSTANCE ); + + this.register(new PatternSet() + .include( + // Web markup + "**/*.htm", + "**/*.html", + "**/*.xhtml", + + // Extended HTML + "**/*.vue", + "**/*.svelte", + + // Data formats + "**/*.xml", + + // Image formats + "**/*.svg" + ), + XmlStyleHeaderComment.INSTANCE + ); } /** * Registers a header comment implementation for a given file pattern. * - * @param filePattern the file pattern to match files for which the given header comment implementation applies + * @param filePattern the file pattern to match files for which the given header comment implementation applies * @param headerComment the header comment implementation */ public void register(@NotNull PatternSet filePattern, @NotNull HeaderComment headerComment) { diff --git a/src/main/java/dev/yumi/gradle/licenser/api/comment/XmlStyleHeaderComment.kt b/src/main/java/dev/yumi/gradle/licenser/api/comment/XmlStyleHeaderComment.kt new file mode 100644 index 0000000..7ddb5a0 --- /dev/null +++ b/src/main/java/dev/yumi/gradle/licenser/api/comment/XmlStyleHeaderComment.kt @@ -0,0 +1,74 @@ +/* + * Copyright 2023 Yumi Project + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +package dev.yumi.gradle.licenser.api.comment + +private const val COMMENT_START = "" + +/** + * [HeaderComment] implementation for XML-style comments. + * + * @author gdude2002 + * @since 1.1.3 + */ +public open class XmlStyleHeaderComment protected constructor() : HeaderComment { + override fun readHeaderComment(source: String): HeaderComment.Result { + val separator = this.extractLineSeparator(source) + + // Find the first comment block using a blank line + val firstBlock = source.split(separator.repeat(2)).first() + + // Find the start of the comment by its opening characters + val start = firstBlock.indexOf(COMMENT_START) + + if (start != 0) { // If the comment doesn't open on the first character of the block... + // ...check whether all prefixing characters are spaces. + val allWhitespacePrefixed = (0 until start).all { source[it] in arrayOf(' ', separator) } + + if (!allWhitespacePrefixed) { // If not, this isn't a licence header – bail out. + return HeaderComment.Result(0, 0, null, separator) + } + } + + // Find the last character of the comment, including the closing characters. + val end = firstBlock.indexOf(COMMENT_END) + COMMENT_END.length + + if (start < 0 || end < 0) { + // If we can't find the start or end of the block, there's no licence header – bail out. + return HeaderComment.Result(0, 0, null, separator) + } + + // Grab the licence header comment, and split it into lines. + val result: MutableList = source.substring(start, end).split(separator).toMutableList() + + // Remove the first and last lines, as those are simply comment start/end characters, and not the licence text. + result.removeFirst() + result.removeLast() + + // Remove any indents from the licence header text, and return the result. + return HeaderComment.Result(start, end, result.map { it.trimIndent() }, separator) + } + + override fun writeHeaderComment(header: List, separator: String): String = + buildString { // Use a string builder to generate the licence header. + append("$COMMENT_START$separator") + + header.forEach { + append("\t$it$separator") + } + + append(COMMENT_END) + } + + public companion object { + /** Instance of this header comment type. **/ + @JvmField // Otherwise this would be `XmlStyleHeaderComment.Companion.getINSTANCE` + public val INSTANCE: XmlStyleHeaderComment = XmlStyleHeaderComment() + } +} diff --git a/src/test/java/dev/yumi/gradle/licenser/test/comment/XmlStyleHeaderCommentTest.kt b/src/test/java/dev/yumi/gradle/licenser/test/comment/XmlStyleHeaderCommentTest.kt new file mode 100644 index 0000000..ba8da49 --- /dev/null +++ b/src/test/java/dev/yumi/gradle/licenser/test/comment/XmlStyleHeaderCommentTest.kt @@ -0,0 +1,92 @@ +/* + * Copyright 2023 Yumi Project + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +package dev.yumi.gradle.licenser.test.comment + +import dev.yumi.gradle.licenser.api.comment.XmlStyleHeaderComment +import org.junit.jupiter.api.Assertions.* +import org.junit.jupiter.api.Test + +class XmlStyleHeaderCommentTest { + @Test + fun `Parsing with existing header`() { + val result = XmlStyleHeaderComment.INSTANCE.readHeaderComment( + """ + + + + + + + + + + + + + + + + """.trimIndent() + + val result = XmlStyleHeaderComment.INSTANCE.writeHeaderComment( + listOf( + "Sample License Header", + "", + "Yippee", + ), + + "\n" + ) + + assertEquals(expected, result) + } +}