Changed policy for properties from methods like "getURL()""
This commit is contained in:
+25
-20
@@ -34,6 +34,8 @@ import org.jetbrains.kotlin.types.JetType
|
||||
import org.jetbrains.kotlin.types.typeUtil.isBoolean
|
||||
import org.jetbrains.kotlin.types.typeUtil.isUnit
|
||||
import org.jetbrains.kotlin.types.typeUtil.makeNotNullable
|
||||
import org.jetbrains.kotlin.util.capitalizeDecapitalize.capitalizeFirstWord
|
||||
import org.jetbrains.kotlin.util.capitalizeDecapitalize.decapitalizeSmart
|
||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||
import java.beans.Introspector
|
||||
import java.util.*
|
||||
@@ -74,7 +76,7 @@ interface SyntheticJavaPropertyDescriptor : PropertyDescriptor {
|
||||
}
|
||||
|
||||
if (!removePrefix) return methodName
|
||||
val name = Introspector.decapitalize(identifier.removePrefix(prefix))
|
||||
val name = identifier.removePrefix(prefix).decapitalizeSmart()
|
||||
if (!Name.isValidIdentifier(name)) return null
|
||||
return Name.identifier(name)
|
||||
}
|
||||
@@ -94,16 +96,10 @@ class JavaSyntheticExtensionsScope(storageManager: StorageManager) : JetScope by
|
||||
|
||||
private fun syntheticPropertyInClassNotCached(javaClass: JavaClassDescriptor, type: JetType, name: Name): PropertyDescriptor? {
|
||||
if (name.isSpecial()) return null
|
||||
val identifier = name.getIdentifier()
|
||||
val identifier = name.identifier
|
||||
if (identifier.isEmpty()) return null
|
||||
val firstChar = identifier[0]
|
||||
if (!firstChar.isJavaIdentifierStart()) return null
|
||||
if (identifier.length() > 1) {
|
||||
if (firstChar.isUpperCase() != identifier[1].isUpperCase()) return null
|
||||
}
|
||||
else {
|
||||
if (firstChar.isUpperCase()) return null
|
||||
}
|
||||
if (!firstChar.isJavaIdentifierStart() || firstChar.isUpperCase()) return null
|
||||
|
||||
val memberScope = javaClass.getMemberScope(type.getArguments())
|
||||
val getMethod = possibleGetMethodNames(name)
|
||||
@@ -111,6 +107,9 @@ class JavaSyntheticExtensionsScope(storageManager: StorageManager) : JetScope by
|
||||
.flatMap { memberScope.getFunctions(it).asSequence() }
|
||||
.singleOrNull { isGoodGetMethod(it) } ?: return null
|
||||
|
||||
// don't accept "uRL" for "getURL" etc
|
||||
if (SyntheticJavaPropertyDescriptor.propertyNameByGetMethodName(getMethod.name) != name) return null
|
||||
|
||||
val propertyType = getMethod.getReturnType() ?: return null
|
||||
val setMethod = memberScope.getFunctions(setMethodName(getMethod.getName())).singleOrNull { isGoodSetMethod(it, propertyType) }
|
||||
|
||||
@@ -139,13 +138,13 @@ class JavaSyntheticExtensionsScope(storageManager: StorageManager) : JetScope by
|
||||
override fun getSyntheticExtensionProperties(receiverTypes: Collection<JetType>, name: Name): Collection<PropertyDescriptor> {
|
||||
var result: SmartList<PropertyDescriptor>? = null
|
||||
val processedTypes: MutableSet<JetType>? = if (receiverTypes.size() > 1) HashSet<JetType>() else null
|
||||
receiverTypes.forEach {
|
||||
result = collectSyntheticPropertiesByName(result, it.makeNotNullable(), name, processedTypes)
|
||||
for (type in receiverTypes) {
|
||||
result = collectSyntheticPropertiesByName(result, type.makeNotNullable(), name, processedTypes)
|
||||
}
|
||||
return when {
|
||||
result == null -> emptyList()
|
||||
result!!.size() > 1 -> result!!.toSet()
|
||||
else -> result!!
|
||||
result.size() > 1 -> result.toSet()
|
||||
else -> result
|
||||
}
|
||||
}
|
||||
|
||||
@@ -202,24 +201,30 @@ class JavaSyntheticExtensionsScope(storageManager: StorageManager) : JetScope by
|
||||
//TODO: reuse code with generation?
|
||||
|
||||
private fun possibleGetMethodNames(propertyName: Name): Collection<Name> {
|
||||
val identifier = propertyName.getIdentifier()
|
||||
val getPrefixName = Name.identifier("get" + identifier.capitalize())
|
||||
val result = ArrayList<Name>(3)
|
||||
val identifier = propertyName.identifier
|
||||
|
||||
if (identifier.startsWith("is")) {
|
||||
return listOf(propertyName, getPrefixName)
|
||||
result.add(propertyName)
|
||||
}
|
||||
else {
|
||||
return listOf(getPrefixName)
|
||||
|
||||
val capitalize1 = identifier.capitalize()
|
||||
val capitalize2 = identifier.capitalizeFirstWord()
|
||||
result.add(Name.identifier("get" + capitalize1))
|
||||
if (capitalize2 != capitalize1) {
|
||||
result.add(Name.identifier("get" + capitalize2))
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private fun setMethodName(getMethodName: Name): Name {
|
||||
val identifier = getMethodName.getIdentifier()
|
||||
val identifier = getMethodName.identifier
|
||||
val prefix = when {
|
||||
identifier.startsWith("get") -> "get"
|
||||
identifier.startsWith("is") -> "is"
|
||||
else -> throw IllegalArgumentException()
|
||||
}
|
||||
return Name.identifier("set" + identifier.removePrefix(prefix).capitalize())
|
||||
return Name.identifier("set" + identifier.removePrefix(prefix))
|
||||
}
|
||||
|
||||
private class MyPropertyDescriptor(
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.util.capitalizeDecapitalize
|
||||
|
||||
/**
|
||||
* "FooBar" -> "fooBar"
|
||||
* "FOOBar" -> "fooBar"
|
||||
* "FOO" -> "foo"
|
||||
*/
|
||||
public fun String.decapitalizeSmart(): String {
|
||||
if (isEmpty() || !charAt(0).isUpperCase()) return this
|
||||
|
||||
if (length() == 1 || !charAt(1).isUpperCase()) {
|
||||
return decapitalize()
|
||||
}
|
||||
|
||||
val secondWordStart = (indices.firstOrNull { !charAt(it).isUpperCase() }
|
||||
?: return toLowerCase()) - 1
|
||||
return substring(0, secondWordStart).toLowerCase() + substring(secondWordStart)
|
||||
}
|
||||
|
||||
/**
|
||||
* "fooBar" -> "FOOBar"
|
||||
* "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)
|
||||
}
|
||||
|
||||
+9
-2
@@ -1,13 +1,20 @@
|
||||
// FILE: KotlinFile.kt
|
||||
fun foo(javaClass: JavaClass) {
|
||||
javaClass.URL = javaClass.URL + "/"
|
||||
javaClass.url = javaClass.url + "/"
|
||||
javaClass.htmlFile += "1"
|
||||
|
||||
javaClass.<!UNRESOLVED_REFERENCE!>url<!>
|
||||
javaClass.<!UNRESOLVED_REFERENCE!>URL<!>
|
||||
javaClass.<!UNRESOLVED_REFERENCE!>uRL<!>
|
||||
javaClass.<!UNRESOLVED_REFERENCE!>HTMLFile<!>
|
||||
javaClass.<!UNRESOLVED_REFERENCE!>hTMLFile<!>
|
||||
javaClass.<!UNRESOLVED_REFERENCE!>htmlfile<!>
|
||||
}
|
||||
|
||||
// FILE: JavaClass.java
|
||||
public class JavaClass {
|
||||
public String getURL() { return true; }
|
||||
public void setURL(String value) { }
|
||||
|
||||
public String getHTMLFile() { return true; }
|
||||
public void setHTMLFile(String value) { }
|
||||
}
|
||||
@@ -5,8 +5,10 @@ internal 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 getHTMLFile(): kotlin.String!
|
||||
public open fun getURL(): kotlin.String!
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public open fun setHTMLFile(/*0*/ value: kotlin.String!): kotlin.Unit
|
||||
public open fun setURL(/*0*/ value: kotlin.String!): kotlin.Unit
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@ fun Thread.foo(urlConnection: java.net.URLConnection) {
|
||||
|
||||
// EXIST_JAVA_ONLY: { lookupString: "priority", itemText: "priority", tailText: " (from getPriority()/setPriority())", typeText: "Int" }
|
||||
// EXIST_JAVA_ONLY: { lookupString: "isDaemon", itemText: "isDaemon", tailText: " (from isDaemon()/setDaemon())", typeText: "Boolean" }
|
||||
// EXIST_JAVA_ONLY: { lookupString: "URL", itemText: "URL", tailText: " (from getURL())", typeText: "URL!" }
|
||||
// EXIST_JAVA_ONLY: { lookupString: "url", itemText: "url", tailText: " (from getURL())", typeText: "URL!" }
|
||||
// ABSENT: getPriority
|
||||
// ABSENT: setPriority
|
||||
// ABSENT: { itemText: "isDaemon", tailText: "()" }
|
||||
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
import java.net.URLConnection
|
||||
|
||||
fun foo(url<caret>){}
|
||||
|
||||
// EXIST_JAVA_ONLY: { lookupString: "urlConnection", itemText: "urlConnection: URLConnection", tailText: " (java.net)" }
|
||||
// ABSENT: urlconnection
|
||||
+6
@@ -1617,6 +1617,12 @@ public class JSBasicCompletionTestGenerated extends AbstractJSBasicCompletionTes
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("URLConnection.kt")
|
||||
public void testURLConnection() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/parameterNameAndType/URLConnection.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("UserPrefix1.kt")
|
||||
public void testUserPrefix1() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/parameterNameAndType/UserPrefix1.kt");
|
||||
|
||||
+6
@@ -1617,6 +1617,12 @@ public class JvmBasicCompletionTestGenerated extends AbstractJvmBasicCompletionT
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("URLConnection.kt")
|
||||
public void testURLConnection() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/parameterNameAndType/URLConnection.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("UserPrefix1.kt")
|
||||
public void testUserPrefix1() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/parameterNameAndType/UserPrefix1.kt");
|
||||
|
||||
@@ -26,6 +26,7 @@ import org.jetbrains.kotlin.types.ErrorUtils
|
||||
import org.jetbrains.kotlin.types.JetType
|
||||
import org.jetbrains.kotlin.types.TypeUtils
|
||||
import org.jetbrains.kotlin.types.checker.JetTypeChecker
|
||||
import org.jetbrains.kotlin.util.capitalizeDecapitalize.decapitalizeSmart
|
||||
import java.util.*
|
||||
import java.util.regex.Pattern
|
||||
|
||||
@@ -240,12 +241,12 @@ public object KotlinNameSuggester {
|
||||
val upperCaseLetter = Character.isUpperCase(c)
|
||||
|
||||
if (i == 0) {
|
||||
addName(if (startLowerCase) decapitalize(s) else s, validator)
|
||||
addName(if (startLowerCase) s.decapitalizeSmart() else s, validator)
|
||||
}
|
||||
else {
|
||||
if (upperCaseLetter && !upperCaseLetterBefore) {
|
||||
val substring = s.substring(i)
|
||||
addName(if (startLowerCase) decapitalize(substring) else substring, validator)
|
||||
addName(if (startLowerCase) substring.decapitalizeSmart() else substring, validator)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -253,27 +254,6 @@ public object KotlinNameSuggester {
|
||||
}
|
||||
}
|
||||
|
||||
private fun decapitalize(s: String): String {
|
||||
var c = s.charAt(0)
|
||||
if (!Character.isUpperCase(c)) return s
|
||||
|
||||
val builder = StringBuilder(s.length())
|
||||
var decapitalize = true
|
||||
for (i in 0..s.length() - 1) {
|
||||
c = s.charAt(i)
|
||||
if (decapitalize) {
|
||||
if (Character.isUpperCase(c)) {
|
||||
c = Character.toLowerCase(c)
|
||||
}
|
||||
else {
|
||||
decapitalize = false
|
||||
}
|
||||
}
|
||||
builder.append(c)
|
||||
}
|
||||
return builder.toString()
|
||||
}
|
||||
|
||||
private fun deleteNonLetterFromString(s: String): String {
|
||||
val pattern = Pattern.compile("[^a-zA-Z]")
|
||||
val matcher = pattern.matcher(s)
|
||||
|
||||
Reference in New Issue
Block a user