|
| 1 | +package de.joshuagleitze.stringnotation |
| 2 | + |
| 3 | +import java.util.* |
| 4 | +import java.util.stream.IntStream |
| 5 | +import javax.lang.model.SourceVersion |
| 6 | + |
| 7 | +/** |
| 8 | + * A notation for Java type names. This notation is like [UpperCamelCase], but when [printing][StringNotation.print], it will drop any |
| 9 | + * character that is not allowed in a Java identifier. If the result is a Java keyword, `_` will be appended to it. |
| 10 | + * |
| 11 | + * Allowed characters are determined using [Character.isJavaIdentifierStart] and [Character.isJavaIdentifierPart]. Keywords are detected |
| 12 | + * using [SourceVersion.isKeyword]. |
| 13 | + */ |
| 14 | +object JavaTypeName: StringNotation by UpperCamelCase { |
| 15 | + override fun print(word: Word) = UpperCamelCase.print(word).makeValidJavaIdentifier() |
| 16 | + |
| 17 | + override fun toString() = this::class.java.simpleName!! |
| 18 | +} |
| 19 | + |
| 20 | +/** |
| 21 | + * A notation for java member names. This notation is like [LowerCamelCase], but when [printing][StringNotation.print], it will drop any |
| 22 | + * character that is not allowed in a Java identifier. If the result is a Java keyword, `_` will be appended to it. |
| 23 | + * |
| 24 | + * Allowed characters are determined using [Character.isJavaIdentifierStart] and [Character.isJavaIdentifierPart]. Keywords are detected |
| 25 | + * using [SourceVersion.isKeyword]. |
| 26 | + */ |
| 27 | +object JavaMemberName: BaseStringNotation(camelCaseSplitRegex) { |
| 28 | + override fun transformPartAfterParse(index: Int, part: String) = part.toLowerCase(Locale.ROOT) |
| 29 | + |
| 30 | + override fun print(word: Word) = word.parts |
| 31 | + .foldIndexed(StringBuffer()) { index, left, right -> |
| 32 | + val rightPart = |
| 33 | + if (left.contains(Regex("[a-zA-Z]"))) right.toFirstUpperOtherLowerCase() |
| 34 | + else right.toLowerCase() |
| 35 | + left.append(printBeforePart(index, rightPart)).append(rightPart) |
| 36 | + }.toString().makeValidJavaIdentifier() |
| 37 | +} |
| 38 | + |
| 39 | +/** |
| 40 | + * A notation for java package parts. When [printing][StringNotation.print], it simply concatenates all word parts and drops any character |
| 41 | + * that is not allowed in a Java identifier. If the result is a Java keyword, `_` will be appended to it. When |
| 42 | + * [parsing][StringNotation.parse], the notation will recognise word parts both in the [LowerCamelCase] and the [SnakeCase] notation. |
| 43 | + * However, neither notation is conventional and parsing will usually yield only one word part on real-world inputs. |
| 44 | + * |
| 45 | + * Allowed characters are determined using [Character.isJavaIdentifierStart] and [Character.isJavaIdentifierPart]. Keywords are detected |
| 46 | + * using [SourceVersion.isKeyword]. |
| 47 | + */ |
| 48 | +object JavaPackagePart: BaseStringNotation(Regex("_|${camelCaseSplitRegex.pattern}")) { |
| 49 | + override fun transformPartAfterParse(index: Int, part: String) = part.toLowerCase(Locale.ROOT) |
| 50 | + |
| 51 | + override fun transformPartToPrint(index: Int, part: String) = part.toLowerCase(Locale.ROOT) |
| 52 | + |
| 53 | + override fun print(word: Word) = super.print(word).makeValidJavaIdentifier() |
| 54 | +} |
| 55 | + |
| 56 | +/** |
| 57 | + * A notation for whole java packages. When [printing][StringNotation.print] parts, it will drop any character that is not allowed in a Java |
| 58 | + * identifier. If the result is a Java keyword, `_` will be appended to it. |
| 59 | + * |
| 60 | + * Allowed characters are determined using [Character.isJavaIdentifierStart] and [Character.isJavaIdentifierPart]. Keywords are detected |
| 61 | + * using [SourceVersion.isKeyword]. |
| 62 | + */ |
| 63 | +object JavaPackageName: BaseStringNotation(Regex("\\.")) { |
| 64 | + override fun transformPartToPrint(index: Int, part: String) = part.toLowerCase(Locale.ROOT).makeValidJavaIdentifier() |
| 65 | + |
| 66 | + override fun printBeforeInnerPart(index: Int, part: String) = "." |
| 67 | +} |
| 68 | + |
| 69 | +/** |
| 70 | + * A notation for `static final` fields in Java. This notation is like [ScreamingSnakeCase], but when [printing][StringNotation.print], it |
| 71 | + * will drop any character that is not allowed in a Java identifier. If the result is a Java keyword, `_` will be appended to it. |
| 72 | + * |
| 73 | + * Allowed characters are determined using [Character.isJavaIdentifierStart] and [Character.isJavaIdentifierPart]. Keywords are detected |
| 74 | + * using [SourceVersion.isKeyword]. |
| 75 | + */ |
| 76 | +object JavaConstantName: StringNotation by ScreamingSnakeCase { |
| 77 | + override fun print(word: Word) = ScreamingSnakeCase.print(word).makeValidJavaIdentifier() |
| 78 | + |
| 79 | + override fun toString() = this::class.java.simpleName!! |
| 80 | +} |
| 81 | + |
| 82 | +private fun String.makeValidJavaIdentifier() = this.keepOnlyJavaIdentifierChars().neutralizeJavaReservedKeywords().ifEmpty { "__" } |
| 83 | + |
| 84 | +private fun String.keepOnlyJavaIdentifierChars() = this.chars() |
| 85 | + .skipWhile { !Character.isJavaIdentifierStart(it) } |
| 86 | + .filter { Character.isJavaIdentifierPart(it) } |
| 87 | + .collect({ StringBuilder() }, { left, right -> left.appendCodePoint(right) }, { left, right -> left.append(right) }) |
| 88 | + .toString() |
| 89 | + |
| 90 | +private fun String.neutralizeJavaReservedKeywords() = if (SourceVersion.isKeyword(this)) this + "_" else this |
| 91 | + |
| 92 | +private inline fun IntStream.skipWhile(crossinline condition: (Int) -> Boolean): IntStream { |
| 93 | + var found = false |
| 94 | + return this.filter { |
| 95 | + if (!found) { |
| 96 | + found = !condition(it) |
| 97 | + } |
| 98 | + found |
| 99 | + } |
| 100 | +} |
| 101 | + |
0 commit comments