diff --git a/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/AndroidXmlHandler.kt b/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/AndroidXmlHandler.kt index 9bcea5dbe9a..3426754882e 100644 --- a/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/AndroidXmlHandler.kt +++ b/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/AndroidXmlHandler.kt @@ -22,7 +22,6 @@ import org.xml.sax.helpers.DefaultHandler import java.util.HashMap class AndroidXmlHandler(private val elementCallback: (ResourceIdentifier, String) -> Unit) : DefaultHandler() { - override fun startDocument() { super.startDocument() } @@ -40,13 +39,10 @@ class AndroidXmlHandler(private val elementCallback: (ResourceIdentifier, String if (name != null) elementCallback(name, widgetType) } - override fun endElement(uri: String?, localName: String, qName: String) { - - } - + override fun endElement(uri: String?, localName: String, qName: String) {} } -fun Attributes.toMap(): HashMap { +private fun Attributes.toMap(): HashMap { val res = HashMap() for (index in 0..length - 1) { val attrName = getLocalName(index)!! diff --git a/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/codegen/AndroidClassType.kt b/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/codegen/AndroidClassType.kt new file mode 100644 index 00000000000..e54eec260f8 --- /dev/null +++ b/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/codegen/AndroidClassType.kt @@ -0,0 +1,68 @@ +/* + * Copyright 2010-2017 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.android.synthetic.codegen + +import org.jetbrains.kotlin.android.synthetic.AndroidConst +import org.jetbrains.kotlin.descriptors.ClassifierDescriptor +import org.jetbrains.kotlin.load.java.lazy.descriptors.LazyJavaClassDescriptor +import org.jetbrains.kotlin.resolve.DescriptorUtils +import org.jetbrains.kotlin.resolve.lazy.descriptors.LazyClassDescriptor + +enum class AndroidClassType(className: String, val supportsCache: Boolean = false, val fragment: Boolean = false) { + ACTIVITY(AndroidConst.ACTIVITY_FQNAME, supportsCache = true), + FRAGMENT(AndroidConst.FRAGMENT_FQNAME, supportsCache = true, fragment = true), + DIALOG(AndroidConst.DIALOG_FQNAME, supportsCache = false), + SUPPORT_FRAGMENT_ACTIVITY(AndroidConst.SUPPORT_FRAGMENT_ACTIVITY_FQNAME, supportsCache = true), + SUPPORT_FRAGMENT(AndroidConst.SUPPORT_FRAGMENT_FQNAME, supportsCache = true, fragment = true), + VIEW(AndroidConst.VIEW_FQNAME), + UNKNOWN(""); + + val internalClassName: String = className.replace('.', '/') + + companion object { + fun getClassType(descriptor: ClassifierDescriptor): AndroidClassType { + fun getClassTypeInternal(name: String): AndroidClassType? = when (name) { + AndroidConst.ACTIVITY_FQNAME -> AndroidClassType.ACTIVITY + AndroidConst.FRAGMENT_FQNAME -> AndroidClassType.FRAGMENT + AndroidConst.DIALOG_FQNAME -> AndroidClassType.DIALOG + AndroidConst.SUPPORT_FRAGMENT_ACTIVITY_FQNAME -> AndroidClassType.SUPPORT_FRAGMENT_ACTIVITY + AndroidConst.SUPPORT_FRAGMENT_FQNAME -> AndroidClassType.SUPPORT_FRAGMENT + AndroidConst.VIEW_FQNAME -> AndroidClassType.VIEW + else -> null + } + + if (descriptor is LazyJavaClassDescriptor) { + val androidClassType = getClassTypeInternal(DescriptorUtils.getFqName(descriptor).asString()) + if (androidClassType != null) return androidClassType + } + else if (descriptor is LazyClassDescriptor) { // For tests (FakeActivity) + val androidClassType = getClassTypeInternal(DescriptorUtils.getFqName(descriptor).toString()) + if (androidClassType != null) return androidClassType + } + + for (supertype in descriptor.typeConstructor.supertypes) { + val declarationDescriptor = supertype.constructor.declarationDescriptor + if (declarationDescriptor != null) { + val androidClassType = getClassType(declarationDescriptor) + if (androidClassType != AndroidClassType.UNKNOWN) return androidClassType + } + } + + return AndroidClassType.UNKNOWN + } + } +} \ No newline at end of file diff --git a/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/codegen/AndroidExpressionCodegenExtension.kt b/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/codegen/AndroidExpressionCodegenExtension.kt index 199aa435693..b4348e6b1f9 100644 --- a/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/codegen/AndroidExpressionCodegenExtension.kt +++ b/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/codegen/AndroidExpressionCodegenExtension.kt @@ -26,18 +26,14 @@ import org.jetbrains.kotlin.codegen.ImplementationBodyCodegen import org.jetbrains.kotlin.codegen.StackValue import org.jetbrains.kotlin.codegen.extensions.ExpressionCodegenExtension import org.jetbrains.kotlin.codegen.state.GenerationState -import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper import org.jetbrains.kotlin.descriptors.* -import org.jetbrains.kotlin.load.java.lazy.descriptors.LazyJavaClassDescriptor import org.jetbrains.kotlin.psi.KtClassOrObject import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassOrAny import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin -import org.jetbrains.kotlin.resolve.lazy.descriptors.LazyClassDescriptor import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue import org.jetbrains.kotlin.resolve.source.KotlinSourceElement -import org.jetbrains.kotlin.types.lowerIfFlexible import org.jetbrains.org.objectweb.asm.Label import org.jetbrains.org.objectweb.asm.Opcodes.ACC_PRIVATE import org.jetbrains.org.objectweb.asm.Opcodes.ACC_PUBLIC @@ -45,64 +41,23 @@ import org.jetbrains.org.objectweb.asm.Opcodes.ACC_SYNTHETIC import org.jetbrains.org.objectweb.asm.Type import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter -enum class AndroidClassType(className: String, val supportsCache: Boolean = false, val fragment: Boolean = false) { - ACTIVITY(AndroidConst.ACTIVITY_FQNAME, supportsCache = true), - FRAGMENT(AndroidConst.FRAGMENT_FQNAME, supportsCache = true, fragment = true), - DIALOG(AndroidConst.DIALOG_FQNAME, supportsCache = false), - SUPPORT_FRAGMENT_ACTIVITY(AndroidConst.SUPPORT_FRAGMENT_ACTIVITY_FQNAME, supportsCache = true), - SUPPORT_FRAGMENT(AndroidConst.SUPPORT_FRAGMENT_FQNAME, supportsCache = true, fragment = true), - VIEW(AndroidConst.VIEW_FQNAME), - UNKNOWN(""); - - val internalClassName: String = className.replace('.', '/') - - companion object { - fun getClassType(descriptor: ClassifierDescriptor): AndroidClassType { - fun getClassTypeInternal(name: String): AndroidClassType? = when (name) { - AndroidConst.ACTIVITY_FQNAME -> AndroidClassType.ACTIVITY - AndroidConst.FRAGMENT_FQNAME -> AndroidClassType.FRAGMENT - AndroidConst.DIALOG_FQNAME -> AndroidClassType.DIALOG - AndroidConst.SUPPORT_FRAGMENT_ACTIVITY_FQNAME -> AndroidClassType.SUPPORT_FRAGMENT_ACTIVITY - AndroidConst.SUPPORT_FRAGMENT_FQNAME -> AndroidClassType.SUPPORT_FRAGMENT - AndroidConst.VIEW_FQNAME -> AndroidClassType.VIEW - else -> null - } - - if (descriptor is LazyJavaClassDescriptor) { - val androidClassType = getClassTypeInternal(DescriptorUtils.getFqName(descriptor).asString()) - if (androidClassType != null) return androidClassType - } - else if (descriptor is LazyClassDescriptor) { // For tests (FakeActivity) - val androidClassType = getClassTypeInternal(DescriptorUtils.getFqName(descriptor).toString()) - if (androidClassType != null) return androidClassType - } - - for (supertype in descriptor.typeConstructor.supertypes) { - val declarationDescriptor = supertype.constructor.declarationDescriptor - if (declarationDescriptor != null) { - val androidClassType = getClassType(declarationDescriptor) - if (androidClassType != AndroidClassType.UNKNOWN) return androidClassType - } - } - - return AndroidClassType.UNKNOWN - } - } -} - class AndroidExpressionCodegenExtension : ExpressionCodegenExtension { companion object { private val PROPERTY_NAME = "_\$_findViewCache" - private val CACHED_FIND_VIEW_BY_ID_METHOD_NAME = "_\$_findCachedViewById" + val CACHED_FIND_VIEW_BY_ID_METHOD_NAME = "_\$_findCachedViewById" val CLEAR_CACHE_METHOD_NAME = "_\$_clearFindViewByIdCache" val ON_DESTROY_METHOD_NAME = "onDestroyView" - fun isCacheSupported(receiverDescriptor: ClassDescriptor, descriptor: PropertyDescriptor? = null): Boolean { - val receiverIsKotlinClass = receiverDescriptor.source is KotlinSourceElement - return receiverIsKotlinClass && when (descriptor) { - is AndroidSyntheticProperty -> descriptor.cacheView - else -> true + fun doesContainerSupportCache(container: ClassDescriptor): Boolean { + return container.source is KotlinSourceElement + } + + fun shouldCacheResource(container: ClassDescriptor, resource: PropertyDescriptor): Boolean { + if (!doesContainerSupportCache(container)) { + return false } + + return (resource as? AndroidSyntheticProperty)?.shouldBeCached ?: false } } @@ -116,43 +71,45 @@ class AndroidExpressionCodegenExtension : ExpressionCodegenExtension { override fun applyProperty(receiver: StackValue, resolvedCall: ResolvedCall<*>, c: ExpressionCodegenExtension.Context): StackValue? { val resultingDescriptor = resolvedCall.resultingDescriptor return if (resultingDescriptor is PropertyDescriptor) { - return generateSyntheticPropertyCall(receiver, resolvedCall, c, resultingDescriptor) + return generateResourcePropertyCall(receiver, resolvedCall, c, resultingDescriptor) } else null } override fun applyFunction(receiver: StackValue, resolvedCall: ResolvedCall<*>, c: ExpressionCodegenExtension.Context): StackValue? { - val resultingDescriptor = resolvedCall.resultingDescriptor - return if (resultingDescriptor is FunctionDescriptor) { - return generateSyntheticFunctionCall(receiver, resolvedCall, c, resultingDescriptor) + val targetCallable = resolvedCall.resultingDescriptor + resolvedCall.resultingDescriptor as? AndroidSyntheticFunction ?: return null + + return if (targetCallable.name.asString() == AndroidConst.CLEAR_FUNCTION_NAME) { + val container = resolvedCall.getReceiverDeclarationDescriptor() as? ClassDescriptor ?: return null + generateClearFindViewByIdCacheFunctionCall(receiver, container, c) + } + else { + null } - else null } - private fun generateSyntheticFunctionCall( + private fun generateClearFindViewByIdCacheFunctionCall( receiver: StackValue, - resolvedCall: ResolvedCall<*>, - c: ExpressionCodegenExtension.Context, - functionDescriptor: FunctionDescriptor + container: ClassDescriptor, + c: ExpressionCodegenExtension.Context ): StackValue? { - if (functionDescriptor !is AndroidSyntheticFunction) return null - if (functionDescriptor.name.asString() != AndroidConst.CLEAR_FUNCTION_NAME) return null + if (!doesContainerSupportCache(container)) { + return StackValue.functionCall(Type.VOID_TYPE) {} + } - val receiverDescriptor = resolvedCall.getReceiverDeclarationDescriptor() as? ClassDescriptor ?: return null - if (!isCacheSupported(receiverDescriptor)) return StackValue.functionCall(Type.VOID_TYPE) {} - - val androidClassType = AndroidClassType.getClassType(receiverDescriptor) + val androidClassType = AndroidClassType.getClassType(container) if (androidClassType == AndroidClassType.UNKNOWN) return null return StackValue.functionCall(Type.VOID_TYPE) { - val bytecodeClassName = c.typeMapper.mapType(receiverDescriptor).internalName + val bytecodeClassName = c.typeMapper.mapType(container).internalName - receiver.put(c.typeMapper.mapType(receiverDescriptor), it) + receiver.put(c.typeMapper.mapType(container), it) it.invokevirtual(bytecodeClassName, CLEAR_CACHE_METHOD_NAME, "()V", false) } } - private fun generateSyntheticPropertyCall( + private fun generateResourcePropertyCall( receiver: StackValue, resolvedCall: ResolvedCall<*>, c: ExpressionCodegenExtension.Context, @@ -164,84 +121,7 @@ class AndroidExpressionCodegenExtension : ExpressionCodegenExtension { val receiverDescriptor = resolvedCall.getReceiverDeclarationDescriptor() as? ClassDescriptor ?: return null val androidClassType = AndroidClassType.getClassType(receiverDescriptor) - return SyntheticProperty(receiver, c.typeMapper, descriptor, receiverDescriptor, androidClassType, androidPackage) - } - - private class SyntheticProperty( - val receiver: StackValue, - val typeMapper: KotlinTypeMapper, - val propertyDescriptor: PropertyDescriptor, - val receiverDescriptor: ClassDescriptor, - val androidClassType: AndroidClassType, - val androidPackage: String - ) : StackValue(typeMapper.mapType(propertyDescriptor.returnType!!)) { - - override fun putSelector(type: Type, v: InstructionAdapter) { - val returnTypeString = typeMapper.mapType(propertyDescriptor.type.lowerIfFlexible()).className - if (AndroidConst.FRAGMENT_FQNAME == returnTypeString || AndroidConst.SUPPORT_FRAGMENT_FQNAME == returnTypeString) { - return putSelectorForFragment(v) - } - - val syntheticProperty = propertyDescriptor as AndroidSyntheticProperty - - if (androidClassType.supportsCache && isCacheSupported(receiverDescriptor, propertyDescriptor)) { - val declarationDescriptorType = typeMapper.mapType(receiverDescriptor) - receiver.put(declarationDescriptorType, v) - - val resourceId = syntheticProperty.resource.id - val packageName = resourceId.packageName ?: androidPackage - v.getstatic(packageName.replace(".", "/") + "/R\$id", resourceId.name, "I") - - v.invokevirtual(declarationDescriptorType.internalName, CACHED_FIND_VIEW_BY_ID_METHOD_NAME, "(I)Landroid/view/View;", false) - } - else { - when (androidClassType) { - AndroidClassType.ACTIVITY, AndroidClassType.SUPPORT_FRAGMENT_ACTIVITY, AndroidClassType.VIEW, AndroidClassType.DIALOG -> { - receiver.put(Type.getType("L${androidClassType.internalClassName};"), v) - getResourceId(v) - v.invokevirtual(androidClassType.internalClassName, "findViewById", "(I)Landroid/view/View;", false) - } - AndroidClassType.FRAGMENT, AndroidClassType.SUPPORT_FRAGMENT -> { - receiver.put(Type.getType("L${androidClassType.internalClassName};"), v) - v.invokevirtual(androidClassType.internalClassName, "getView", "()Landroid/view/View;", false) - getResourceId(v) - v.invokevirtual("android/view/View", "findViewById", "(I)Landroid/view/View;", false) - } - else -> throw IllegalStateException("Invalid Android class type: $androidClassType") // Should never occur - } - } - - v.checkcast(this.type) - } - - private fun putSelectorForFragment(v: InstructionAdapter) { - receiver.put(Type.getType("L${androidClassType.internalClassName};"), v) - - when (androidClassType) { - AndroidClassType.ACTIVITY, AndroidClassType.FRAGMENT -> { - v.invokevirtual(androidClassType.internalClassName, "getFragmentManager", "()Landroid/app/FragmentManager;", false) - getResourceId(v) - v.invokevirtual("android/app/FragmentManager", "findFragmentById", "(I)Landroid/app/Fragment;", false) - } - AndroidClassType.SUPPORT_FRAGMENT -> { - v.invokevirtual(androidClassType.internalClassName, "getFragmentManager", "()Landroid/support/v4/app/FragmentManager;", false) - getResourceId(v) - v.invokevirtual("android/support/v4/app/FragmentManager", "findFragmentById", "(I)Landroid/support/v4/app/Fragment;", false) - } - AndroidClassType.SUPPORT_FRAGMENT_ACTIVITY -> { - v.invokevirtual(androidClassType.internalClassName, "getSupportFragmentManager", "()Landroid/support/v4/app/FragmentManager;", false) - getResourceId(v) - v.invokevirtual("android/support/v4/app/FragmentManager", "findFragmentById", "(I)Landroid/support/v4/app/Fragment;", false) - } - else -> throw IllegalStateException("Invalid Android class type: $androidClassType") // Should never occur - } - - v.checkcast(this.type) - } - - fun getResourceId(v: InstructionAdapter) { - v.getstatic(androidPackage.replace(".", "/") + "/R\$id", propertyDescriptor.name.asString(), "I") - } + return ResourcePropertyStackValue(receiver, c.typeMapper, descriptor, receiverDescriptor, androidClassType, androidPackage) } private fun ResolvedCall<*>.getReceiverDeclarationDescriptor(): ClassifierDescriptor? { @@ -250,18 +130,16 @@ class AndroidExpressionCodegenExtension : ExpressionCodegenExtension { override fun generateClassSyntheticParts(codegen: ImplementationBodyCodegen) { val classBuilder = codegen.v - val state = codegen.state - val classOrObject = codegen.myClass - val descriptor = codegen.descriptor + val targetClass = codegen.myClass as? KtClassOrObject ?: return + val descriptor = codegen.descriptor if (descriptor.kind != ClassKind.CLASS || descriptor.isInner || DescriptorUtils.isLocal(descriptor)) return - if (classOrObject !is KtClassOrObject) return // works only only on physical classes, not synthetic ones // Do not generate anything if class is not supported val androidClassType = AndroidClassType.getClassType(descriptor) if (androidClassType == AndroidClassType.UNKNOWN) return - val context = SyntheticPartsGenerateContext(classBuilder, state, descriptor, classOrObject, androidClassType) + val context = SyntheticPartsGenerateContext(classBuilder, codegen.state, descriptor, targetClass, androidClassType) context.generateCachedFindViewByIdFunction() context.generateClearCacheFunction() diff --git a/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/codegen/AndroidOnDestroyClassBuilderInterceptorExtension.kt b/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/codegen/AndroidOnDestroyClassBuilderInterceptorExtension.kt index 2b5aae2ef0d..9216145f7a5 100644 --- a/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/codegen/AndroidOnDestroyClassBuilderInterceptorExtension.kt +++ b/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/codegen/AndroidOnDestroyClassBuilderInterceptorExtension.kt @@ -21,7 +21,6 @@ import org.jetbrains.kotlin.codegen.ClassBuilder import org.jetbrains.kotlin.codegen.ClassBuilderFactory import org.jetbrains.kotlin.codegen.DelegatingClassBuilder import org.jetbrains.kotlin.codegen.extensions.ClassBuilderInterceptorExtension -import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.diagnostics.DiagnosticSink import org.jetbrains.kotlin.psi.KtClass import org.jetbrains.kotlin.resolve.BindingContext @@ -107,12 +106,12 @@ class AndroidOnDestroyClassBuilderInterceptorExtension : ClassBuilderInterceptor private fun generateClearCacheMethodCall() { if (name != AndroidExpressionCodegenExtension.ON_DESTROY_METHOD_NAME || currentClass == null) return - if (Type.getArgumentTypes(desc).size != 0) return + if (Type.getArgumentTypes(desc).isNotEmpty()) return if (Type.getReturnType(desc) != Type.VOID_TYPE) return val classType = currentClassName?.let { Type.getObjectType(it) } ?: return - val descriptor = bindingContext.get(BindingContext.CLASS, currentClass) as? ClassDescriptor ?: return + val descriptor = bindingContext.get(BindingContext.CLASS, currentClass) ?: return val androidClassType = AndroidClassType.getClassType(descriptor) if (!androidClassType.fragment) return diff --git a/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/codegen/ResourcePropertyStackValue.kt b/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/codegen/ResourcePropertyStackValue.kt new file mode 100644 index 00000000000..c1f9228e602 --- /dev/null +++ b/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/codegen/ResourcePropertyStackValue.kt @@ -0,0 +1,104 @@ +/* + * Copyright 2010-2017 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.android.synthetic.codegen + +import org.jetbrains.kotlin.android.synthetic.AndroidConst +import org.jetbrains.kotlin.android.synthetic.res.AndroidSyntheticProperty +import org.jetbrains.kotlin.codegen.StackValue +import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper +import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.PropertyDescriptor +import org.jetbrains.kotlin.types.lowerIfFlexible +import org.jetbrains.org.objectweb.asm.Type +import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter + +class ResourcePropertyStackValue( + val receiver: StackValue, + val typeMapper: KotlinTypeMapper, + val propertyDescriptor: PropertyDescriptor, + val receiverDescriptor: ClassDescriptor, + val androidClassType: AndroidClassType, + val androidPackage: String +) : StackValue(typeMapper.mapType(propertyDescriptor.returnType!!)) { + + override fun putSelector(type: Type, v: InstructionAdapter) { + val returnTypeString = typeMapper.mapType(propertyDescriptor.type.lowerIfFlexible()).className + if (AndroidConst.FRAGMENT_FQNAME == returnTypeString || AndroidConst.SUPPORT_FRAGMENT_FQNAME == returnTypeString) { + return putSelectorForFragment(v) + } + + val syntheticProperty = propertyDescriptor as AndroidSyntheticProperty + + if (androidClassType.supportsCache && AndroidExpressionCodegenExtension.shouldCacheResource(receiverDescriptor, propertyDescriptor)) { + val declarationDescriptorType = typeMapper.mapType(receiverDescriptor) + receiver.put(declarationDescriptorType, v) + + val resourceId = syntheticProperty.resource.id + val packageName = resourceId.packageName ?: androidPackage + v.getstatic(packageName.replace(".", "/") + "/R\$id", resourceId.name, "I") + + v.invokevirtual(declarationDescriptorType.internalName, AndroidExpressionCodegenExtension.CACHED_FIND_VIEW_BY_ID_METHOD_NAME, "(I)Landroid/view/View;", false) + } + else { + when (androidClassType) { + AndroidClassType.ACTIVITY, AndroidClassType.SUPPORT_FRAGMENT_ACTIVITY, AndroidClassType.VIEW, AndroidClassType.DIALOG -> { + receiver.put(Type.getType("L${androidClassType.internalClassName};"), v) + getResourceId(v) + v.invokevirtual(androidClassType.internalClassName, "findViewById", "(I)Landroid/view/View;", false) + } + AndroidClassType.FRAGMENT, AndroidClassType.SUPPORT_FRAGMENT -> { + receiver.put(Type.getType("L${androidClassType.internalClassName};"), v) + v.invokevirtual(androidClassType.internalClassName, "getView", "()Landroid/view/View;", false) + getResourceId(v) + v.invokevirtual("android/view/View", "findViewById", "(I)Landroid/view/View;", false) + } + else -> throw IllegalStateException("Invalid Android class type: $androidClassType") // Should never occur + } + } + + v.checkcast(this.type) + } + + private fun putSelectorForFragment(v: InstructionAdapter) { + receiver.put(Type.getType("L${androidClassType.internalClassName};"), v) + + when (androidClassType) { + AndroidClassType.ACTIVITY, AndroidClassType.FRAGMENT -> { + v.invokevirtual(androidClassType.internalClassName, "getFragmentManager", "()Landroid/app/FragmentManager;", false) + getResourceId(v) + v.invokevirtual("android/app/FragmentManager", "findFragmentById", "(I)Landroid/app/Fragment;", false) + } + AndroidClassType.SUPPORT_FRAGMENT -> { + v.invokevirtual(androidClassType.internalClassName, "getFragmentManager", "()Landroid/support/v4/app/FragmentManager;", false) + getResourceId(v) + v.invokevirtual("android/support/v4/app/FragmentManager", "findFragmentById", "(I)Landroid/support/v4/app/Fragment;", false) + } + AndroidClassType.SUPPORT_FRAGMENT_ACTIVITY -> { + v.invokevirtual(androidClassType.internalClassName, "getSupportFragmentManager", "()Landroid/support/v4/app/FragmentManager;", false) + getResourceId(v) + v.invokevirtual("android/support/v4/app/FragmentManager", "findFragmentById", "(I)Landroid/support/v4/app/Fragment;", false) + } + else -> throw IllegalStateException("Invalid Android class type: $androidClassType") // Should never occur + } + + v.checkcast(this.type) + } + + fun getResourceId(v: InstructionAdapter) { + v.getstatic(androidPackage.replace(".", "/") + "/R\$id", propertyDescriptor.name.asString(), "I") + } +} \ No newline at end of file diff --git a/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/descriptors/AndroidSyntheticPackageFragmentDescriptor.kt b/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/descriptors/AndroidSyntheticPackageFragmentDescriptor.kt index a9a1a2a4989..76681a7e406 100644 --- a/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/descriptors/AndroidSyntheticPackageFragmentDescriptor.kt +++ b/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/descriptors/AndroidSyntheticPackageFragmentDescriptor.kt @@ -32,7 +32,6 @@ import org.jetbrains.kotlin.utils.Printer import java.util.* class AndroidSyntheticPackageData( - val layoutName: String, val moduleData: AndroidModuleData, val forView: Boolean, val isDeprecated: Boolean, diff --git a/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/descriptors/PredefinedPackageFragmentDescriptor.kt b/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/descriptors/PredefinedPackageFragmentDescriptor.kt index 5fc1761dda7..4f274800a58 100644 --- a/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/descriptors/PredefinedPackageFragmentDescriptor.kt +++ b/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/descriptors/PredefinedPackageFragmentDescriptor.kt @@ -41,18 +41,20 @@ class PredefinedPackageFragmentDescriptor( } private val scope = PredefinedScope() + override fun getMemberScope() = scope inner class PredefinedScope : MemberScopeImpl() { - @Suppress("UNCHECKED_CAST") override fun getContributedVariables(name: Name, location: LookupLocation) = emptyList() - @Suppress("UNCHECKED_CAST") - override fun getContributedFunctions(name: Name, location: LookupLocation) = - calculatedFunctions().filter { it.name == name } + override fun getContributedFunctions(name: Name, location: LookupLocation) = calculatedFunctions().filter { it.name == name } - override fun getContributedDescriptors(kindFilter: DescriptorKindFilter, nameFilter: (Name) -> Boolean) = - calculatedFunctions().filter { nameFilter(it.name) && kindFilter.accepts(it) } + override fun getContributedDescriptors( + kindFilter: DescriptorKindFilter, + nameFilter: (Name) -> Boolean + ): List { + return calculatedFunctions().filter { nameFilter(it.name) && kindFilter.accepts(it) } + } override fun printScopeStructure(p: Printer) { p.println(this::class.java.simpleName) diff --git a/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/diagnostic/AndroidExtensionPropertiesCallChecker.kt b/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/diagnostic/AndroidExtensionPropertiesCallChecker.kt index 4da0b4e405c..8535b359644 100644 --- a/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/diagnostic/AndroidExtensionPropertiesCallChecker.kt +++ b/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/diagnostic/AndroidExtensionPropertiesCallChecker.kt @@ -20,6 +20,7 @@ import com.intellij.psi.PsiElement import org.jetbrains.kotlin.android.synthetic.descriptors.AndroidSyntheticPackageFragmentDescriptor import org.jetbrains.kotlin.android.synthetic.diagnostic.ErrorsAndroid.* import org.jetbrains.kotlin.android.synthetic.res.AndroidSyntheticProperty +import org.jetbrains.kotlin.android.synthetic.res.isErrorType import org.jetbrains.kotlin.descriptors.PropertyDescriptor import org.jetbrains.kotlin.diagnostics.DiagnosticSink import org.jetbrains.kotlin.psi.KtDotQualifiedExpression diff --git a/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/res/AndroidLayoutXmlFileManager.kt b/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/res/AndroidLayoutXmlFileManager.kt index d5c4fd63243..58a32542eb2 100644 --- a/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/res/AndroidLayoutXmlFileManager.kt +++ b/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/res/AndroidLayoutXmlFileManager.kt @@ -29,15 +29,17 @@ import org.jetbrains.kotlin.descriptors.ModuleDescriptor import org.jetbrains.kotlin.descriptors.PropertyDescriptor import java.util.* -class AndroidVariantData(val variant: AndroidVariant, private val layouts: Map>): Map> by layouts -class AndroidModuleData(val module: AndroidModule, private val variants: List): Iterable by variants { +class AndroidVariantData(val variant: AndroidVariant, val layouts: Map>) + +class AndroidModuleData(val module: AndroidModule, val variants: List) { companion object { val EMPTY = AndroidModuleData(AndroidModule("android", listOf()), listOf()) } } -abstract class AndroidLayoutXmlFileManager(val project: Project) { +data class AndroidLayoutGroupData(val name: String, val layouts: List) +abstract class AndroidLayoutXmlFileManager(val project: Project) { abstract val androidModule: AndroidModule? open fun propertyToXmlAttributes(propertyDescriptor: PropertyDescriptor): List = listOf() @@ -47,7 +49,7 @@ abstract class AndroidLayoutXmlFileManager(val project: Project) { return AndroidModuleData(androidModule, androidModule.variants.map { getVariantData(it) }) } - fun getVariantData(variant: AndroidVariant): AndroidVariantData { + private fun getVariantData(variant: AndroidVariant): AndroidVariantData { val psiManager = PsiManager.getInstance(project) val fileManager = VirtualFileManager.getInstance() @@ -84,11 +86,11 @@ abstract class AndroidLayoutXmlFileManager(val project: Project) { return AndroidVariantData(variant, layoutNameToXmlFiles) } - fun extractResources(files: List, module: ModuleDescriptor): List { - return filterDuplicates(doExtractResources(files, module)) + fun extractResources(layoutGroupFiles: AndroidLayoutGroupData, module: ModuleDescriptor): List { + return filterDuplicates(doExtractResources(layoutGroupFiles, module)) } - protected abstract fun doExtractResources(files: List, module: ModuleDescriptor): List + protected abstract fun doExtractResources(layoutGroup: AndroidLayoutGroupData, module: ModuleDescriptor): AndroidLayoutGroup protected fun parseAndroidResource(id: ResourceIdentifier, tag: String, sourceElement: PsiElement?): AndroidResource { return when (tag) { @@ -98,41 +100,39 @@ abstract class AndroidLayoutXmlFileManager(val project: Project) { } } - private fun filterDuplicates(layoutGroups: List): List { + private fun filterDuplicates(layoutGroup: AndroidLayoutGroup): List { val resourceMap = linkedMapOf() val resourcesToExclude = hashSetOf() - for (layoutGroup in layoutGroups) { - val resources = layoutGroup.layouts.flatMap { it.resources }.groupBy { - val id = it.id - if (id.packageName == null) id.name else id.packageName + "/" + id.name - } + val resourcesByName = layoutGroup.layouts.flatMap { it.resources }.groupBy { + val id = it.id + if (id.packageName == null) id.name else id.packageName + "/" + id.name + } - for (resources in resources.values) { - val isPartiallyDefined = resources.size < layoutGroup.layouts.size + for (resources in resourcesByName.values) { + val isPartiallyDefined = resources.size < layoutGroup.layouts.size - for (res in resources) { - if (resourceMap.contains(res.id.name)) { - val existing = resourceMap[res.id.name]!! + for (res in resources) { + if (res.id.name in resourceMap) { + val existing = resourceMap[res.id.name]!! - if (!res.sameClass(existing) || res.id.packageName != existing.id.packageName) { - resourcesToExclude.add(res.id.name) - } - else if (res is AndroidResource.Widget && existing is AndroidResource.Widget) { - // Widgets with the same id but different types exist. - if (res.xmlType != existing.xmlType && existing.xmlType != AndroidConst.VIEW_FQNAME) { - val mergedWidget = AndroidResource.Widget( - res.id, AndroidConst.VIEW_FQNAME, res.sourceElement, isPartiallyDefined) - resourceMap.put(res.id.name, mergedWidget) - } + if (!res.sameClass(existing) || res.id.packageName != existing.id.packageName) { + resourcesToExclude.add(res.id.name) + } + else if (res is AndroidResource.Widget && existing is AndroidResource.Widget) { + // Widgets with the same id but different types exist. + if (res.xmlType != existing.xmlType && existing.xmlType != AndroidConst.VIEW_FQNAME) { + val mergedWidget = AndroidResource.Widget( + res.id, AndroidConst.VIEW_FQNAME, res.sourceElement, isPartiallyDefined) + resourceMap.put(res.id.name, mergedWidget) } } - else if (isPartiallyDefined) { - resourceMap.put(res.id.name, res.partiallyDefined()) - } - else { - resourceMap.put(res.id.name, res) - } + } + else if (isPartiallyDefined) { + resourceMap.put(res.id.name, res.partiallyDefined()) + } + else { + resourceMap.put(res.id.name, res) } } } @@ -141,12 +141,10 @@ abstract class AndroidLayoutXmlFileManager(val project: Project) { return resourceMap.values.toList() } - companion object { fun getInstance(module: Module): AndroidLayoutXmlFileManager? { val service = ModuleServiceManager.getService(module, AndroidLayoutXmlFileManager::class.java) return service ?: module.getComponent(AndroidLayoutXmlFileManager::class.java) } } - } diff --git a/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/res/AndroidPackageFragmentProviderExtension.kt b/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/res/AndroidPackageFragmentProviderExtension.kt index 06100e0a12a..2ef39ed43d5 100644 --- a/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/res/AndroidPackageFragmentProviderExtension.kt +++ b/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/res/AndroidPackageFragmentProviderExtension.kt @@ -48,11 +48,11 @@ abstract class AndroidPackageFragmentProviderExtension : PackageFragmentProvider val packagesToLookupInCompletion = arrayListOf() // Packages with synthetic properties - for (variantData in moduleData) { - for ((layoutName, layouts) in variantData) { + for (variantData in moduleData.variants) { + for ((layoutName, layouts) in variantData.layouts) { fun createPackageFragment(fqName: String, forView: Boolean, isDeprecated: Boolean = false) { - val resources = layoutXmlFileManager.extractResources(layouts, module) - val packageData = AndroidSyntheticPackageData(layoutName, moduleData, forView, isDeprecated, resources) + val resources = layoutXmlFileManager.extractResources(AndroidLayoutGroupData(layoutName, layouts), module) + val packageData = AndroidSyntheticPackageData(moduleData, forView, isDeprecated, resources) val packageDescriptor = AndroidSyntheticPackageFragmentDescriptor( module, FqName(fqName), packageData, lazyContext, storageManager) packagesToLookupInCompletion += packageDescriptor @@ -71,7 +71,7 @@ abstract class AndroidPackageFragmentProviderExtension : PackageFragmentProvider allPackageDescriptors += PredefinedPackageFragmentDescriptor(s, module, storageManager) } - for (variantData in moduleData) { + for (variantData in moduleData.variants) { val fqName = AndroidConst.SYNTHETIC_PACKAGE + '.' + variantData.variant.name allPackageDescriptors += PredefinedPackageFragmentDescriptor(fqName, module, storageManager) } @@ -79,7 +79,9 @@ abstract class AndroidPackageFragmentProviderExtension : PackageFragmentProvider // Package with clearFindViewByIdCache() AndroidConst.SYNTHETIC_SUBPACKAGES.last().let { s -> val packageDescriptor = PredefinedPackageFragmentDescriptor(s, module, storageManager, packagesToLookupInCompletion) { descriptor -> - lazyContext().getWidgetReceivers(false).filter { it.mayHaveCache }.map { genClearCacheFunction(descriptor, it.type) } + lazyContext().getWidgetReceivers(false) + .filter { it.mayHaveCache } + .map { genClearCacheFunction(descriptor, it.type) } } packagesToLookupInCompletion += packageDescriptor allPackageDescriptors += packageDescriptor @@ -89,14 +91,13 @@ abstract class AndroidPackageFragmentProviderExtension : PackageFragmentProvider } } -class AndroidSyntheticPackageFragmentProvider( - val packageFragments: Collection -) : PackageFragmentProvider { +class AndroidSyntheticPackageFragmentProvider(val packageFragments: Collection) : PackageFragmentProvider { override fun getPackageFragments(fqName: FqName) = packageFragments.filter { it.fqName == fqName } - override fun getSubPackagesOf(fqName: FqName, nameFilter: (Name) -> Boolean) = - packageFragments.asSequence() - .map { it.fqName } - .filter { !it.isRoot && it.parent() == fqName } - .toList() + override fun getSubPackagesOf(fqName: FqName, nameFilter: (Name) -> Boolean): List { + return packageFragments.asSequence() + .map { it.fqName } + .filter { !it.isRoot && it.parent() == fqName } + .toList() + } } diff --git a/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/res/CliAndroidLayoutXmlFileManager.kt b/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/res/CliAndroidLayoutXmlFileManager.kt index 5dbdfa71d45..22aef294fb6 100644 --- a/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/res/CliAndroidLayoutXmlFileManager.kt +++ b/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/res/CliAndroidLayoutXmlFileManager.kt @@ -17,7 +17,6 @@ package org.jetbrains.kotlin.android.synthetic.res import com.intellij.openapi.project.Project -import com.intellij.psi.PsiFile import org.jetbrains.kotlin.android.synthetic.AndroidXmlHandler import org.jetbrains.kotlin.descriptors.ModuleDescriptor import java.io.ByteArrayInputStream @@ -33,24 +32,19 @@ class CliAndroidLayoutXmlFileManager( private val saxParser: SAXParser = initSAX() - override fun doExtractResources(files: List, module: ModuleDescriptor): List { - val layoutGroupFiles = files.groupBy { it.name } - val layoutGroups = mutableListOf() + override fun doExtractResources(layoutGroup: AndroidLayoutGroupData, module: ModuleDescriptor): AndroidLayoutGroup { + val layouts = layoutGroup.layouts.map { layout -> + val resources = arrayListOf() - for ((name, layouts) in layoutGroupFiles) { - layoutGroups += AndroidLayoutGroup(name, layouts.map { layout -> - val resources = arrayListOf() - - val inputStream = ByteArrayInputStream(layout.virtualFile.contentsToByteArray()) - saxParser.parse(inputStream, AndroidXmlHandler { id, tag -> - resources += parseAndroidResource(id, tag, null) - }) - - AndroidLayout(resources) + val inputStream = ByteArrayInputStream(layout.virtualFile.contentsToByteArray()) + saxParser.parse(inputStream, AndroidXmlHandler { id, tag -> + resources += parseAndroidResource(id, tag, null) }) + + AndroidLayout(resources) } - return layoutGroups + return AndroidLayoutGroup(layoutGroup.name, layouts) } private fun initSAX(): SAXParser { diff --git a/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/res/syntheticDescriptorGeneration.kt b/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/res/syntheticDescriptorGeneration.kt index a0482c1a0f0..7ddee6a8480 100644 --- a/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/res/syntheticDescriptorGeneration.kt +++ b/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/res/syntheticDescriptorGeneration.kt @@ -61,11 +61,14 @@ internal fun genPropertyForWidget( val classDescriptor = resolvedWidget.viewClassDescriptor val type = classDescriptor?.let { val defaultType = classDescriptor.defaultType - if (defaultType.constructor.parameters.isEmpty()) + + if (defaultType.constructor.parameters.isEmpty()) { defaultType - else - KotlinTypeFactory.simpleNotNullType(Annotations.EMPTY, classDescriptor, - defaultType.constructor.parameters.map(::StarProjectionImpl)) + } + else { + KotlinTypeFactory.simpleNotNullType( + Annotations.EMPTY, classDescriptor, defaultType.constructor.parameters.map(::StarProjectionImpl)) + } } ?: context.viewType return genProperty(resolvedWidget.widget, receiverType, type, packageFragmentDescriptor, sourceEl, resolvedWidget.errorType) @@ -89,8 +92,6 @@ private fun genProperty( sourceElement: SourceElement, errorType: String? ): PropertyDescriptor { - val cacheView = type.constructor.declarationDescriptor?.fqNameUnsafe?.asString() != AndroidConst.VIEWSTUB_FQNAME - val property = object : AndroidSyntheticProperty, PropertyDescriptorImpl( containingDeclaration, null, @@ -101,7 +102,7 @@ private fun genProperty( Name.identifier(resource.id.name), CallableMemberDescriptor.Kind.SYNTHESIZED, sourceElement, - /* lateinit = */ false, + /* lateInit = */ false, /* isConst = */ false, /* isHeader = */ false, /* isImpl = */ false, @@ -109,7 +110,7 @@ private fun genProperty( /* isDelegated = */ false ) { override val errorType = errorType - override val cacheView = cacheView + override val shouldBeCached = type.shouldBeCached override val resource = resource } @@ -126,11 +127,11 @@ private fun genProperty( Annotations.EMPTY, Modality.FINAL, Visibilities.PUBLIC, - false, - false, - false, + /* isDefault = */ false, + /* isExternal = */ false, + /* isInline = */ false, CallableMemberDescriptor.Kind.SYNTHESIZED, - null, + /* original = */ null, SourceElement.NO_SOURCE ) @@ -141,13 +142,26 @@ private fun genProperty( return property } +private val SimpleType.shouldBeCached: Boolean + get() { + val viewClassFqName = constructor.declarationDescriptor?.fqNameUnsafe?.asString() ?: return false + return when (viewClassFqName) { + AndroidConst.VIEWSTUB_FQNAME -> false + else -> true + } + } + interface AndroidSyntheticFunction interface AndroidSyntheticProperty { - val errorType: String? - val cacheView: Boolean val resource: AndroidResource - val isErrorType: Boolean - get() = errorType != null + val errorType: String? + + // True if the View should be cached. + // Some views (such as ViewStub) should not be cached. + val shouldBeCached: Boolean } + +val AndroidSyntheticProperty.isErrorType: Boolean + get() = errorType != null \ No newline at end of file diff --git a/plugins/android-extensions/android-extensions-idea/src/org/jetbrains/kotlin/android/synthetic/idea/AndroidGotoDeclarationHandler.kt b/plugins/android-extensions/android-extensions-idea/src/org/jetbrains/kotlin/android/synthetic/idea/AndroidGotoDeclarationHandler.kt index 4e578c5ef61..9a65a3e3794 100644 --- a/plugins/android-extensions/android-extensions-idea/src/org/jetbrains/kotlin/android/synthetic/idea/AndroidGotoDeclarationHandler.kt +++ b/plugins/android-extensions/android-extensions-idea/src/org/jetbrains/kotlin/android/synthetic/idea/AndroidGotoDeclarationHandler.kt @@ -33,7 +33,6 @@ import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode class AndroidGotoDeclarationHandler : GotoDeclarationHandler { - override fun getGotoDeclarationTargets(sourceElement: PsiElement?, offset: Int, editor: Editor?): Array? { if (sourceElement is LeafPsiElement && sourceElement.parent is KtSimpleNameExpression) { val simpleNameExpression = sourceElement.parent as? KtSimpleNameExpression ?: return null diff --git a/plugins/android-extensions/android-extensions-idea/src/org/jetbrains/kotlin/android/synthetic/idea/res/IDEAndroidLayoutXmlFileManager.kt b/plugins/android-extensions/android-extensions-idea/src/org/jetbrains/kotlin/android/synthetic/idea/res/IDEAndroidLayoutXmlFileManager.kt index dec4d9c7cbe..eeec93776e1 100644 --- a/plugins/android-extensions/android-extensions-idea/src/org/jetbrains/kotlin/android/synthetic/idea/res/IDEAndroidLayoutXmlFileManager.kt +++ b/plugins/android-extensions/android-extensions-idea/src/org/jetbrains/kotlin/android/synthetic/idea/res/IDEAndroidLayoutXmlFileManager.kt @@ -21,7 +21,6 @@ import com.android.tools.idea.gradle.AndroidGradleModel import com.intellij.openapi.module.Module import com.intellij.openapi.roots.ProjectRootModificationTracker import com.intellij.psi.PsiElement -import com.intellij.psi.PsiFile import com.intellij.psi.impl.PsiTreeChangePreprocessor import com.intellij.psi.util.CachedValue import com.intellij.psi.util.CachedValueProvider @@ -33,10 +32,11 @@ import org.jetbrains.kotlin.android.synthetic.res.* import org.jetbrains.kotlin.descriptors.ModuleDescriptor import org.jetbrains.kotlin.descriptors.PropertyDescriptor import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe +import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstance class IDEAndroidLayoutXmlFileManager(val module: Module) : AndroidLayoutXmlFileManager(module.project) { override val androidModule: AndroidModule? - get() = module.androidFacet?.toAndroidModuleInfo() + get() = AndroidFacet.getInstance(module)?.let { getAndroidModuleInfo(it) } @Volatile private var _moduleData: CachedValue? = null @@ -58,27 +58,21 @@ class IDEAndroidLayoutXmlFileManager(val module: Module) : AndroidLayoutXmlFileM } private fun getPsiTreeChangePreprocessor(): PsiTreeChangePreprocessor { - return project.getExtensions(PsiTreeChangePreprocessor.EP_NAME).first { it is AndroidPsiTreeChangePreprocessor } + return project.getExtensions(PsiTreeChangePreprocessor.EP_NAME).firstIsInstance() } - override fun doExtractResources(files: List, module: ModuleDescriptor): List { - val layoutGroupFiles = files.groupBy { it.name } - val layoutGroups = mutableListOf() - - for ((name, layouts) in layoutGroupFiles) { - layoutGroups += AndroidLayoutGroup(name, layouts.map { layout -> - val resources = arrayListOf() - layout.accept(AndroidXmlVisitor { id, widgetType, attribute -> - resources += parseAndroidResource(id, widgetType, attribute.valueElement) - }) - AndroidLayout(resources) + override fun doExtractResources(layoutGroup: AndroidLayoutGroupData, module: ModuleDescriptor): AndroidLayoutGroup { + val layouts = layoutGroup.layouts.map { layout -> + val resources = arrayListOf() + layout.accept(AndroidXmlVisitor { id, widgetType, attribute -> + resources += parseAndroidResource(id, widgetType, attribute.valueElement) }) + AndroidLayout(resources) } - return layoutGroups + return AndroidLayoutGroup(layoutGroup.name, layouts) } - override fun propertyToXmlAttributes(propertyDescriptor: PropertyDescriptor): List { val fqPath = propertyDescriptor.fqNameUnsafe.pathSegments() if (fqPath.size <= SYNTHETIC_PACKAGE_PATH_LENGTH) return listOf() @@ -87,7 +81,7 @@ class IDEAndroidLayoutXmlFileManager(val module: Module) : AndroidLayoutXmlFileM val layoutNamePosition = SYNTHETIC_PACKAGE_PATH_LENGTH + (if (defaultVariant) 0 else 1) val layoutName = fqPath[layoutNamePosition].asString() - val layoutFiles = variantData[layoutName] ?: return null + val layoutFiles = variantData.layouts[layoutName] ?: return null if (layoutFiles.isEmpty()) return null val propertyName = propertyDescriptor.name.asString() @@ -101,7 +95,7 @@ class IDEAndroidLayoutXmlFileManager(val module: Module) : AndroidLayoutXmlFileM return attributes } - for (variantData in getModuleData()) { + for (variantData in getModuleData().variants) { if (variantData.variant.isMainVariant && fqPath.size == SYNTHETIC_PACKAGE_PATH_LENGTH + 2) { handle(variantData, true)?.let { return it } } @@ -115,16 +109,13 @@ class IDEAndroidLayoutXmlFileManager(val module: Module) : AndroidLayoutXmlFileM private fun SourceProvider.toVariant() = AndroidVariant(name, resDirectories.map { it.canonicalPath }) - private val Module.androidFacet: AndroidFacet? - get() = AndroidFacet.getInstance(this) + private fun getAndroidModuleInfo(androidFacet: AndroidFacet): AndroidModule? { + val applicationPackage = androidFacet.manifest?.`package`?.toString() ?: return null - private fun AndroidFacet.toAndroidModuleInfo(): AndroidModule? { - val applicationPackage = manifest?.`package`?.toString() ?: return null + val allResDirectories = androidFacet.getAppResources(true)?.resourceDirs.orEmpty().mapNotNull { it.canonicalPath } - val allResDirectories = getAppResources(true)?.resourceDirs.orEmpty().mapNotNull { it.canonicalPath } - - val resDirectoriesForMainVariant = run { - val resDirsFromSourceProviders = AndroidGradleModel.get(module)?.allSourceProviders.orEmpty() + val resDirectoriesForMainVariant = androidFacet.run { + val resDirsFromSourceProviders = AndroidGradleModel.get(this.module)?.allSourceProviders.orEmpty() .filter { it.name != "main" } .flatMap { it.resDirectories } .map { it.canonicalPath } @@ -134,7 +125,7 @@ class IDEAndroidLayoutXmlFileManager(val module: Module) : AndroidLayoutXmlFileM val variants = mutableListOf(AndroidVariant("main", resDirectoriesForMainVariant)) - AndroidGradleModel.get(module)?.let { androidGradleModel -> + AndroidGradleModel.get(androidFacet.module)?.let { androidGradleModel -> androidGradleModel.activeSourceProviders.filter { it.name != "main" }.forEach { sourceProvider -> variants += sourceProvider.toVariant() }