From 8f1a3043de1819a5d63c55204814ff1449281697 Mon Sep 17 00:00:00 2001 From: Valentin Kipyatkov Date: Mon, 28 Sep 2015 21:59:21 +0300 Subject: [PATCH] Synthetic properties made locale-independant too --- .../synthetic/JavaSyntheticPropertiesScope.kt | 9 +-- .../javaProperties/OnlyAscii.kt | 22 +++++++ .../javaProperties/OnlyAscii.txt | 13 ++++ .../checkers/JetDiagnosticsTestGenerated.java | 6 ++ .../kotlin/utils/capitalizeDecapitalize.kt | 64 +++++++++++++++---- 5 files changed, 98 insertions(+), 16 deletions(-) create mode 100644 compiler/testData/diagnostics/tests/syntheticExtensions/javaProperties/OnlyAscii.kt create mode 100644 compiler/testData/diagnostics/tests/syntheticExtensions/javaProperties/OnlyAscii.txt diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/synthetic/JavaSyntheticPropertiesScope.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/synthetic/JavaSyntheticPropertiesScope.kt index 5d6ac561514..9a36e0c325e 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/synthetic/JavaSyntheticPropertiesScope.kt +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/synthetic/JavaSyntheticPropertiesScope.kt @@ -34,6 +34,7 @@ import org.jetbrains.kotlin.types.* import org.jetbrains.kotlin.types.typeUtil.isBoolean import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf import org.jetbrains.kotlin.types.typeUtil.isUnit +import org.jetbrains.kotlin.util.capitalizeDecapitalize.capitalizeAsciiOnly import org.jetbrains.kotlin.util.capitalizeDecapitalize.capitalizeFirstWord import org.jetbrains.kotlin.util.capitalizeDecapitalize.decapitalizeSmart import org.jetbrains.kotlin.utils.Printer @@ -79,7 +80,7 @@ interface SyntheticJavaPropertyDescriptor : PropertyDescriptor { } if (!removePrefix) return methodName - val name = identifier.removePrefix(prefix).decapitalizeSmart() + val name = identifier.removePrefix(prefix).decapitalizeSmart(asciiOnly = true) if (!Name.isValidIdentifier(name)) return null return Name.identifier(name) } @@ -96,7 +97,7 @@ class JavaSyntheticPropertiesScope(storageManager: StorageManager) : JetScopeImp val identifier = name.identifier if (identifier.isEmpty()) return null val firstChar = identifier[0] - if (!firstChar.isJavaIdentifierStart() || firstChar.isUpperCase()) return null + if (!firstChar.isJavaIdentifierStart() || firstChar in 'A'..'Z') return null val memberScope = ownerClass.unsubstitutedMemberScope val getMethod = possibleGetMethodNames(name) @@ -221,8 +222,8 @@ class JavaSyntheticPropertiesScope(storageManager: StorageManager) : JetScopeImp result.add(propertyName) } - val capitalize1 = identifier.capitalize() - val capitalize2 = identifier.capitalizeFirstWord() + val capitalize1 = identifier.capitalizeAsciiOnly() + val capitalize2 = identifier.capitalizeFirstWord(asciiOnly = true) result.add(Name.identifier("get" + capitalize1)) if (capitalize2 != capitalize1) { result.add(Name.identifier("get" + capitalize2)) diff --git a/compiler/testData/diagnostics/tests/syntheticExtensions/javaProperties/OnlyAscii.kt b/compiler/testData/diagnostics/tests/syntheticExtensions/javaProperties/OnlyAscii.kt new file mode 100644 index 00000000000..182df416a0f --- /dev/null +++ b/compiler/testData/diagnostics/tests/syntheticExtensions/javaProperties/OnlyAscii.kt @@ -0,0 +1,22 @@ +// FILE: KotlinFile.kt +fun foo(javaClass: JavaClass) { + javaClass.КакоеТоСвойство = javaClass.КакоеТоСвойство + 1 + javaClass.какоеТоСвойство + + javaClass.КУ + javaClass.ку +} + +// FILE: JavaClass.java +public class JavaClass { + public int getКакоеТоСвойство() { + return 0; + } + + public void setКакоеТоСвойство(int value) { + } + + public int getКУ() { + return 0; + } +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/syntheticExtensions/javaProperties/OnlyAscii.txt b/compiler/testData/diagnostics/tests/syntheticExtensions/javaProperties/OnlyAscii.txt new file mode 100644 index 00000000000..d8470607c84 --- /dev/null +++ b/compiler/testData/diagnostics/tests/syntheticExtensions/javaProperties/OnlyAscii.txt @@ -0,0 +1,13 @@ +package + +public fun foo(/*0*/ javaClass: JavaClass): kotlin.Unit + +public open class JavaClass { + public constructor JavaClass() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open fun getКУ(): kotlin.Int + public open fun getКакоеТоСвойство(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open fun setКакоеТоСвойство(/*0*/ value: kotlin.Int): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java index 7bdd5b1a4e8..d4ff2a9f881 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java @@ -15059,6 +15059,12 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest { doTest(fileName); } + @TestMetadata("OnlyAscii.kt") + public void testOnlyAscii() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/syntheticExtensions/javaProperties/OnlyAscii.kt"); + doTest(fileName); + } + @TestMetadata("OnlyPublic.kt") public void testOnlyPublic() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/syntheticExtensions/javaProperties/OnlyPublic.kt"); diff --git a/core/util.runtime/src/org/jetbrains/kotlin/utils/capitalizeDecapitalize.kt b/core/util.runtime/src/org/jetbrains/kotlin/utils/capitalizeDecapitalize.kt index 3d2c4c17523..e6c1f5d7dce 100644 --- a/core/util.runtime/src/org/jetbrains/kotlin/utils/capitalizeDecapitalize.kt +++ b/core/util.runtime/src/org/jetbrains/kotlin/utils/capitalizeDecapitalize.kt @@ -21,16 +21,23 @@ package org.jetbrains.kotlin.util.capitalizeDecapitalize * "FOOBar" -> "fooBar" * "FOO" -> "foo" */ -public fun String.decapitalizeSmart(): String { - if (isEmpty() || !charAt(0).isUpperCase()) return this - - if (length() == 1 || !charAt(1).isUpperCase()) { - return decapitalize() +public fun String.decapitalizeSmart(asciiOnly: Boolean = false): String { + fun isUpperCaseCharAt(index: Int): Boolean { + val c = charAt(index) + return if (asciiOnly) c in 'A'..'Z' else c.isUpperCase() } - val secondWordStart = (indices.firstOrNull { !charAt(it).isUpperCase() } - ?: return toLowerCase()) - 1 - return substring(0, secondWordStart).toLowerCase() + substring(secondWordStart) + if (isEmpty() || !isUpperCaseCharAt(0)) return this + + if (length() == 1 || !isUpperCaseCharAt(1)) { + return if (asciiOnly) decapitalizeAsciiOnly() else decapitalize() + } + + fun toLowerCase(string: String) = if (asciiOnly) string.toLowerCaseAsciiOnly() else string.toLowerCase() + + val secondWordStart = (indices.firstOrNull { !isUpperCaseCharAt(it) } + ?: return toLowerCase(this)) - 1 + return toLowerCase(substring(0, secondWordStart)) + substring(secondWordStart) } /** @@ -38,10 +45,17 @@ public fun String.decapitalizeSmart(): String { * "FooBar" -> "FOOBar" * "foo" -> "FOO" */ -public fun String.capitalizeFirstWord(): String { - val secondWordStart = indices.drop(1).firstOrNull { !charAt(it).isLowerCase() } - ?: return toUpperCase() - return substring(0, secondWordStart).toUpperCase() + substring(secondWordStart) +public fun String.capitalizeFirstWord(asciiOnly: Boolean = false): String { + fun toUpperCase(string: String) = if (asciiOnly) string.toUpperCaseAsciiOnly() else string.toUpperCase() + + fun isLowerCaseCharAt(index: Int): Boolean { + val c = charAt(index) + return if (asciiOnly) c in 'a'..'z' else c.isLowerCase() + } + + val secondWordStart = indices.drop(1).firstOrNull { !isLowerCaseCharAt(it) } + ?: return toUpperCase(this) + return toUpperCase(substring(0, secondWordStart)) + substring(secondWordStart) } public fun String.capitalizeAsciiOnly(): String { @@ -53,4 +67,30 @@ public fun String.capitalizeAsciiOnly(): String { this } +public fun String.decapitalizeAsciiOnly(): String { + if (isEmpty()) return this + val c = charAt(0) + return if (c in 'A'..'Z') + c.toLowerCase() + substring(1) + else + this +} + +public fun String.toLowerCaseAsciiOnly(): String { + val builder = StringBuilder(length()) + for (c in this) { + builder.append(if (c in 'A'..'Z') c.toLowerCase() else c) + } + return builder.toString() +} + +public fun String.toUpperCaseAsciiOnly(): String { + val builder = StringBuilder(length()) + for (c in this) { + builder.append(if (c in 'a'..'z') c.toUpperCase() else c) + } + return builder.toString() +} + +