Create descriptors directly in Android Extensions (compiler plugin)
This commit is contained in:
@@ -2324,6 +2324,15 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Collection<ExpressionCodegenExtension> codegenExtensions = ExpressionCodegenExtension.Companion.getInstances(state.getProject());
|
||||||
|
if (!codegenExtensions.isEmpty()) {
|
||||||
|
ExpressionCodegenExtension.Context context = new ExpressionCodegenExtension.Context(typeMapper, v);
|
||||||
|
for (ExpressionCodegenExtension extension : codegenExtensions) {
|
||||||
|
StackValue stackValue = extension.applyFunction(receiver, resolvedCall, context);
|
||||||
|
if (stackValue != null) return stackValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Callable callable = resolveToCallable(fd, superCall, resolvedCall);
|
Callable callable = resolveToCallable(fd, superCall, resolvedCall);
|
||||||
|
|
||||||
return callable.invokeMethodWithArguments(resolvedCall, receiver, this);
|
return callable.invokeMethodWithArguments(resolvedCall, receiver, this);
|
||||||
@@ -2385,15 +2394,6 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
|||||||
|
|
||||||
if (!(resolvedCall.getResultingDescriptor() instanceof ConstructorDescriptor)) { // otherwise already
|
if (!(resolvedCall.getResultingDescriptor() instanceof ConstructorDescriptor)) { // otherwise already
|
||||||
receiver = StackValue.receiver(resolvedCall, receiver, this, callableMethod);
|
receiver = StackValue.receiver(resolvedCall, receiver, this, callableMethod);
|
||||||
|
|
||||||
Collection<ExpressionCodegenExtension> codegenExtensions = ExpressionCodegenExtension.Companion.getInstances(state.getProject());
|
|
||||||
if (!codegenExtensions.isEmpty()) {
|
|
||||||
ExpressionCodegenExtension.Context context = new ExpressionCodegenExtension.Context(typeMapper, v);
|
|
||||||
for (ExpressionCodegenExtension extension : codegenExtensions) {
|
|
||||||
if (extension.applyFunction(receiver, resolvedCall, context)) return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
receiver.put(receiver.type, v);
|
receiver.put(receiver.type, v);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+5
-5
@@ -25,10 +25,10 @@ import org.jetbrains.kotlin.codegen.ClassBuilder
|
|||||||
import org.jetbrains.kotlin.codegen.state.GenerationState
|
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||||
import org.jetbrains.kotlin.psi.KtClassOrObject
|
import org.jetbrains.kotlin.psi.KtClassOrObject
|
||||||
import org.jetbrains.kotlin.descriptors.*
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
import org.jetbrains.kotlin.resolve.*
|
|
||||||
|
|
||||||
public interface ExpressionCodegenExtension {
|
public interface ExpressionCodegenExtension {
|
||||||
companion object : ProjectExtensionDescriptor<ExpressionCodegenExtension>("org.jetbrains.kotlin.expressionCodegenExtension", javaClass<ExpressionCodegenExtension>())
|
companion object : ProjectExtensionDescriptor<ExpressionCodegenExtension>(
|
||||||
|
"org.jetbrains.kotlin.expressionCodegenExtension", ExpressionCodegenExtension::class.java)
|
||||||
|
|
||||||
public class Context(
|
public class Context(
|
||||||
public val typeMapper: JetTypeMapper,
|
public val typeMapper: JetTypeMapper,
|
||||||
@@ -42,10 +42,10 @@ public interface ExpressionCodegenExtension {
|
|||||||
public fun applyProperty(receiver: StackValue, resolvedCall: ResolvedCall<*>, c: Context): StackValue? = null
|
public fun applyProperty(receiver: StackValue, resolvedCall: ResolvedCall<*>, c: Context): StackValue? = null
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used for generating custom byte code for the function call. This function has non-lazy semantics.
|
* Used for generating custom byte code for the function call. This function has lazy semantics.
|
||||||
* Returns true if the stack was modified.
|
* Returns new stack value.
|
||||||
*/
|
*/
|
||||||
public fun applyFunction(receiver: StackValue, resolvedCall: ResolvedCall<*>, c: Context): Boolean = false
|
public fun applyFunction(receiver: StackValue, resolvedCall: ResolvedCall<*>, c: Context): StackValue? = null
|
||||||
|
|
||||||
public fun generateClassSyntheticParts(
|
public fun generateClassSyntheticParts(
|
||||||
classBuilder: ClassBuilder,
|
classBuilder: ClassBuilder,
|
||||||
|
|||||||
@@ -83,6 +83,7 @@ import org.jetbrains.kotlin.psi.KtFile
|
|||||||
import org.jetbrains.kotlin.resolve.CodeAnalyzerInitializer
|
import org.jetbrains.kotlin.resolve.CodeAnalyzerInitializer
|
||||||
import org.jetbrains.kotlin.resolve.jvm.KotlinJavaPsiFacade
|
import org.jetbrains.kotlin.resolve.jvm.KotlinJavaPsiFacade
|
||||||
import org.jetbrains.kotlin.resolve.jvm.extensions.AnalysisCompletedHandlerExtension
|
import org.jetbrains.kotlin.resolve.jvm.extensions.AnalysisCompletedHandlerExtension
|
||||||
|
import org.jetbrains.kotlin.resolve.jvm.extensions.PackageFragmentProviderExtension
|
||||||
import org.jetbrains.kotlin.resolve.lazy.declarations.CliDeclarationProviderFactoryService
|
import org.jetbrains.kotlin.resolve.lazy.declarations.CliDeclarationProviderFactoryService
|
||||||
import org.jetbrains.kotlin.resolve.lazy.declarations.DeclarationProviderFactoryService
|
import org.jetbrains.kotlin.resolve.lazy.declarations.DeclarationProviderFactoryService
|
||||||
import org.jetbrains.kotlin.utils.PathUtil
|
import org.jetbrains.kotlin.utils.PathUtil
|
||||||
@@ -139,6 +140,7 @@ public class KotlinCoreEnvironment private constructor(
|
|||||||
ExpressionCodegenExtension.registerExtensionPoint(project)
|
ExpressionCodegenExtension.registerExtensionPoint(project)
|
||||||
ClassBuilderInterceptorExtension.registerExtensionPoint(project)
|
ClassBuilderInterceptorExtension.registerExtensionPoint(project)
|
||||||
AnalysisCompletedHandlerExtension.registerExtensionPoint(project)
|
AnalysisCompletedHandlerExtension.registerExtensionPoint(project)
|
||||||
|
PackageFragmentProviderExtension.registerExtensionPoint(project)
|
||||||
StorageComponentContainerContributor.registerExtensionPoint(project)
|
StorageComponentContainerContributor.registerExtensionPoint(project)
|
||||||
|
|
||||||
for (registrar in configuration.getList(ComponentRegistrar.PLUGIN_COMPONENT_REGISTRARS)) {
|
for (registrar in configuration.getList(ComponentRegistrar.PLUGIN_COMPONENT_REGISTRARS)) {
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ import org.jetbrains.kotlin.load.java.lazy.ModuleClassResolverImpl
|
|||||||
import org.jetbrains.kotlin.load.java.structure.JavaClass
|
import org.jetbrains.kotlin.load.java.structure.JavaClass
|
||||||
import org.jetbrains.kotlin.psi.KtFile
|
import org.jetbrains.kotlin.psi.KtFile
|
||||||
import org.jetbrains.kotlin.resolve.CodeAnalyzerInitializer
|
import org.jetbrains.kotlin.resolve.CodeAnalyzerInitializer
|
||||||
|
import org.jetbrains.kotlin.resolve.jvm.extensions.PackageFragmentProviderExtension
|
||||||
import org.jetbrains.kotlin.resolve.TargetEnvironment
|
import org.jetbrains.kotlin.resolve.TargetEnvironment
|
||||||
import org.jetbrains.kotlin.resolve.TargetPlatform
|
import org.jetbrains.kotlin.resolve.TargetPlatform
|
||||||
import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatform
|
import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatform
|
||||||
@@ -77,9 +78,10 @@ public object JvmAnalyzerFacade : AnalyzerFacade<JvmPlatformParameters>() {
|
|||||||
resolverForModule.componentProvider.get<JavaDescriptorResolver>()
|
resolverForModule.componentProvider.get<JavaDescriptorResolver>()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val trace = CodeAnalyzerInitializer.getInstance(project).createTrace()
|
||||||
val container = createContainerForLazyResolveWithJava(
|
val container = createContainerForLazyResolveWithJava(
|
||||||
moduleContext,
|
moduleContext,
|
||||||
CodeAnalyzerInitializer.getInstance(project).createTrace(),
|
trace,
|
||||||
declarationProviderFactory,
|
declarationProviderFactory,
|
||||||
moduleContentScope,
|
moduleContentScope,
|
||||||
moduleClassResolver,
|
moduleClassResolver,
|
||||||
@@ -89,7 +91,14 @@ public object JvmAnalyzerFacade : AnalyzerFacade<JvmPlatformParameters>() {
|
|||||||
val resolveSession = container.get<ResolveSession>()
|
val resolveSession = container.get<ResolveSession>()
|
||||||
val javaDescriptorResolver = container.get<JavaDescriptorResolver>()
|
val javaDescriptorResolver = container.get<JavaDescriptorResolver>()
|
||||||
|
|
||||||
val providersForModule = listOf(resolveSession.getPackageFragmentProvider(), javaDescriptorResolver.packageFragmentProvider)
|
val providersForModule = arrayListOf(
|
||||||
|
resolveSession.packageFragmentProvider,
|
||||||
|
javaDescriptorResolver.packageFragmentProvider)
|
||||||
|
|
||||||
|
providersForModule += PackageFragmentProviderExtension.getInstances(project)
|
||||||
|
.map { it.getPackageFragmentProvider(project, moduleDescriptor, moduleContext.storageManager, trace, moduleInfo) }
|
||||||
|
.filterNotNull()
|
||||||
|
|
||||||
return ResolverForModule(CompositePackageFragmentProvider(providersForModule), container)
|
return ResolverForModule(CompositePackageFragmentProvider(providersForModule), container)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+7
@@ -43,6 +43,7 @@ import org.jetbrains.kotlin.resolve.BindingContext;
|
|||||||
import org.jetbrains.kotlin.resolve.BindingTrace;
|
import org.jetbrains.kotlin.resolve.BindingTrace;
|
||||||
import org.jetbrains.kotlin.resolve.TopDownAnalysisMode;
|
import org.jetbrains.kotlin.resolve.TopDownAnalysisMode;
|
||||||
import org.jetbrains.kotlin.resolve.jvm.extensions.AnalysisCompletedHandlerExtension;
|
import org.jetbrains.kotlin.resolve.jvm.extensions.AnalysisCompletedHandlerExtension;
|
||||||
|
import org.jetbrains.kotlin.resolve.jvm.extensions.PackageFragmentProviderExtension;
|
||||||
import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatform;
|
import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatform;
|
||||||
import org.jetbrains.kotlin.resolve.lazy.declarations.FileBasedDeclarationProviderFactory;
|
import org.jetbrains.kotlin.resolve.lazy.declarations.FileBasedDeclarationProviderFactory;
|
||||||
|
|
||||||
@@ -135,6 +136,12 @@ public enum TopDownAnalyzerFacadeForJVM {
|
|||||||
}
|
}
|
||||||
additionalProviders.add(container.getJavaDescriptorResolver().getPackageFragmentProvider());
|
additionalProviders.add(container.getJavaDescriptorResolver().getPackageFragmentProvider());
|
||||||
|
|
||||||
|
for (PackageFragmentProviderExtension extension : PackageFragmentProviderExtension.Companion.getInstances(project)) {
|
||||||
|
PackageFragmentProvider provider = extension.getPackageFragmentProvider(
|
||||||
|
project, moduleContext.getModule(), moduleContext.getStorageManager(), trace, null);
|
||||||
|
if (provider != null) additionalProviders.add(provider);
|
||||||
|
}
|
||||||
|
|
||||||
container.getLazyTopDownAnalyzerForTopLevel().analyzeFiles(topDownAnalysisMode, allFiles, additionalProviders);
|
container.getLazyTopDownAnalyzerForTopLevel().analyzeFiles(topDownAnalysisMode, allFiles, additionalProviders);
|
||||||
|
|
||||||
BindingContext bindingContext = trace.getBindingContext();
|
BindingContext bindingContext = trace.getBindingContext();
|
||||||
|
|||||||
+39
@@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
* 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.resolve.jvm.extensions
|
||||||
|
|
||||||
|
import com.intellij.openapi.project.Project
|
||||||
|
import org.jetbrains.kotlin.analyzer.ModuleInfo
|
||||||
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
|
import org.jetbrains.kotlin.extensions.ProjectExtensionDescriptor
|
||||||
|
import org.jetbrains.kotlin.resolve.BindingTrace
|
||||||
|
import org.jetbrains.kotlin.storage.StorageManager
|
||||||
|
|
||||||
|
public interface PackageFragmentProviderExtension {
|
||||||
|
companion object : ProjectExtensionDescriptor<PackageFragmentProviderExtension>(
|
||||||
|
"org.jetbrains.kotlin.packageFragmentProviderExtension",
|
||||||
|
PackageFragmentProviderExtension::class.java
|
||||||
|
)
|
||||||
|
|
||||||
|
public fun getPackageFragmentProvider(
|
||||||
|
project: Project,
|
||||||
|
module: ModuleDescriptor,
|
||||||
|
storageManager: StorageManager,
|
||||||
|
trace: BindingTrace,
|
||||||
|
moduleInfo: ModuleInfo?
|
||||||
|
): PackageFragmentProvider?
|
||||||
|
}
|
||||||
@@ -10,7 +10,7 @@
|
|||||||
<orderEntry type="sourceFolder" forTests="false" />
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
<orderEntry type="module" module-name="plugin-api" exported="" />
|
<orderEntry type="module" module-name="plugin-api" exported="" />
|
||||||
<orderEntry type="module" module-name="frontend" exported="" />
|
<orderEntry type="module" module-name="frontend" exported="" />
|
||||||
<orderEntry type="module" module-name="frontend.java" />
|
<orderEntry type="module" module-name="frontend.java" exported="" />
|
||||||
<orderEntry type="module" module-name="cli" />
|
<orderEntry type="module" module-name="cli" />
|
||||||
<orderEntry type="module" module-name="util" />
|
<orderEntry type="module" module-name="util" />
|
||||||
<orderEntry type="module" module-name="backend" scope="PROVIDED" />
|
<orderEntry type="module" module-name="backend" scope="PROVIDED" />
|
||||||
|
|||||||
+3
-18
@@ -17,10 +17,7 @@
|
|||||||
package org.jetbrains.kotlin.android.synthetic
|
package org.jetbrains.kotlin.android.synthetic
|
||||||
|
|
||||||
import com.intellij.mock.MockProject
|
import com.intellij.mock.MockProject
|
||||||
import com.intellij.openapi.components.ServiceManager
|
|
||||||
import com.intellij.openapi.extensions.Extensions
|
import com.intellij.openapi.extensions.Extensions
|
||||||
import com.intellij.openapi.project.Project
|
|
||||||
import org.jetbrains.kotlin.analyzer.ModuleInfo
|
|
||||||
import org.jetbrains.kotlin.android.synthetic.codegen.AndroidExpressionCodegenExtension
|
import org.jetbrains.kotlin.android.synthetic.codegen.AndroidExpressionCodegenExtension
|
||||||
import org.jetbrains.kotlin.android.synthetic.codegen.AndroidOnDestroyClassBuilderInterceptorExtension
|
import org.jetbrains.kotlin.android.synthetic.codegen.AndroidOnDestroyClassBuilderInterceptorExtension
|
||||||
import org.jetbrains.kotlin.android.synthetic.diagnostic.AndroidExtensionPropertiesCallChecker
|
import org.jetbrains.kotlin.android.synthetic.diagnostic.AndroidExtensionPropertiesCallChecker
|
||||||
@@ -37,10 +34,9 @@ import org.jetbrains.kotlin.config.CompilerConfigurationKey
|
|||||||
import org.jetbrains.kotlin.container.StorageComponentContainer
|
import org.jetbrains.kotlin.container.StorageComponentContainer
|
||||||
import org.jetbrains.kotlin.container.useInstance
|
import org.jetbrains.kotlin.container.useInstance
|
||||||
import org.jetbrains.kotlin.diagnostics.rendering.DefaultErrorMessages
|
import org.jetbrains.kotlin.diagnostics.rendering.DefaultErrorMessages
|
||||||
import org.jetbrains.kotlin.extensions.ExternalDeclarationsProvider
|
|
||||||
import org.jetbrains.kotlin.extensions.StorageComponentContainerContributor
|
import org.jetbrains.kotlin.extensions.StorageComponentContainerContributor
|
||||||
import org.jetbrains.kotlin.psi.KtFile
|
|
||||||
import org.jetbrains.kotlin.resolve.TargetPlatform
|
import org.jetbrains.kotlin.resolve.TargetPlatform
|
||||||
|
import org.jetbrains.kotlin.resolve.jvm.extensions.PackageFragmentProviderExtension
|
||||||
import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatform
|
import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatform
|
||||||
|
|
||||||
public object AndroidConfigurationKeys {
|
public object AndroidConfigurationKeys {
|
||||||
@@ -73,13 +69,6 @@ public class AndroidCommandLineProcessor : CommandLineProcessor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class CliAndroidDeclarationsProvider(private val project: Project) : ExternalDeclarationsProvider {
|
|
||||||
override fun getExternalDeclarations(moduleInfo: ModuleInfo?): Collection<KtFile> {
|
|
||||||
val parser = ServiceManager.getService(project, SyntheticFileGenerator::class.java) as? CliSyntheticFileGenerator
|
|
||||||
return parser?.getSyntheticFiles() ?: listOf()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class AndroidComponentRegistrar : ComponentRegistrar {
|
public class AndroidComponentRegistrar : ComponentRegistrar {
|
||||||
|
|
||||||
public override fun registerProjectComponents(project: MockProject, configuration: CompilerConfiguration) {
|
public override fun registerProjectComponents(project: MockProject, configuration: CompilerConfiguration) {
|
||||||
@@ -87,18 +76,14 @@ public class AndroidComponentRegistrar : ComponentRegistrar {
|
|||||||
val variants = configuration.get(AndroidConfigurationKeys.VARIANT)?.map { parseVariant(it) }?.filterNotNull() ?: emptyList()
|
val variants = configuration.get(AndroidConfigurationKeys.VARIANT)?.map { parseVariant(it) }?.filterNotNull() ?: emptyList()
|
||||||
|
|
||||||
if (variants.isNotEmpty() && !applicationPackage.isNullOrBlank()) {
|
if (variants.isNotEmpty() && !applicationPackage.isNullOrBlank()) {
|
||||||
val xmlProcessor = CliSyntheticFileGenerator(project, applicationPackage!!, variants)
|
val layoutXmlFileManager = CliAndroidLayoutXmlFileManager(project, applicationPackage!!, variants)
|
||||||
|
|
||||||
project.registerService(SyntheticFileGenerator::class.java, xmlProcessor)
|
|
||||||
|
|
||||||
val layoutXmlFileManager = CliAndroidLayoutXmlFileManager(project, applicationPackage, variants)
|
|
||||||
project.registerService(AndroidLayoutXmlFileManager::class.java, layoutXmlFileManager)
|
project.registerService(AndroidLayoutXmlFileManager::class.java, layoutXmlFileManager)
|
||||||
|
|
||||||
ExternalDeclarationsProvider.registerExtension(project, CliAndroidDeclarationsProvider(project))
|
|
||||||
ExpressionCodegenExtension.registerExtension(project, AndroidExpressionCodegenExtension())
|
ExpressionCodegenExtension.registerExtension(project, AndroidExpressionCodegenExtension())
|
||||||
StorageComponentContainerContributor.registerExtension(project, AndroidExtensionPropertiesComponentContainerContributor())
|
StorageComponentContainerContributor.registerExtension(project, AndroidExtensionPropertiesComponentContainerContributor())
|
||||||
Extensions.getRootArea().getExtensionPoint(DefaultErrorMessages.Extension.EP_NAME).registerExtension(DefaultErrorMessagesAndroid())
|
Extensions.getRootArea().getExtensionPoint(DefaultErrorMessages.Extension.EP_NAME).registerExtension(DefaultErrorMessagesAndroid())
|
||||||
ClassBuilderInterceptorExtension.registerExtension(project, AndroidOnDestroyClassBuilderInterceptorExtension())
|
ClassBuilderInterceptorExtension.registerExtension(project, AndroidOnDestroyClassBuilderInterceptorExtension())
|
||||||
|
PackageFragmentProviderExtension.registerExtension(project, CliAndroidPackageFragmentProviderExtension())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+19
-10
@@ -16,20 +16,19 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.android.synthetic
|
package org.jetbrains.kotlin.android.synthetic
|
||||||
|
|
||||||
import com.intellij.openapi.util.Key
|
|
||||||
import org.jetbrains.kotlin.lexer.KtKeywordToken
|
import org.jetbrains.kotlin.lexer.KtKeywordToken
|
||||||
import org.jetbrains.kotlin.lexer.KtTokens
|
import org.jetbrains.kotlin.lexer.KtTokens
|
||||||
|
|
||||||
public object AndroidConst {
|
public object AndroidConst {
|
||||||
val ANDROID_USER_PACKAGE: Key<String> = Key.create<String>("ANDROID_USER_PACKAGE")
|
|
||||||
|
|
||||||
val SYNTHETIC_FILENAME_PREFIX: String = "ANDROIDXML_"
|
|
||||||
val LAYOUT_POSTFIX: String = "_LAYOUT"
|
|
||||||
val VIEW_LAYOUT_POSTFIX: String = "_VIEW"
|
|
||||||
|
|
||||||
val SYNTHETIC_PACKAGE: String = "kotlinx.android.synthetic"
|
val SYNTHETIC_PACKAGE: String = "kotlinx.android.synthetic"
|
||||||
val SYNTHETIC_PACKAGE_PATH_LENGTH = SYNTHETIC_PACKAGE.count { it == '.' } + 1
|
val SYNTHETIC_PACKAGE_PATH_LENGTH = SYNTHETIC_PACKAGE.count { it == '.' } + 1
|
||||||
|
|
||||||
|
val SYNTHETIC_SUBPACKAGES: List<String> = SYNTHETIC_PACKAGE.split('.').fold(arrayListOf<String>()) { list, segment ->
|
||||||
|
val prevSegment = list.lastOrNull()?.let { "$it." } ?: ""
|
||||||
|
list += "$prevSegment$segment"
|
||||||
|
list
|
||||||
|
}
|
||||||
|
|
||||||
val ANDROID_NAMESPACE: String = "android"
|
val ANDROID_NAMESPACE: String = "android"
|
||||||
val ID_ATTRIBUTE_NO_NAMESPACE: String = "id"
|
val ID_ATTRIBUTE_NO_NAMESPACE: String = "id"
|
||||||
val ID_ATTRIBUTE: String = "$ANDROID_NAMESPACE:$ID_ATTRIBUTE_NO_NAMESPACE"
|
val ID_ATTRIBUTE: String = "$ANDROID_NAMESPACE:$ID_ATTRIBUTE_NO_NAMESPACE"
|
||||||
@@ -41,6 +40,9 @@ public object AndroidConst {
|
|||||||
|
|
||||||
val CLEAR_FUNCTION_NAME = "clearFindViewByIdCache"
|
val CLEAR_FUNCTION_NAME = "clearFindViewByIdCache"
|
||||||
|
|
||||||
|
|
||||||
|
//TODO FqName / ClassId
|
||||||
|
|
||||||
val VIEW_FQNAME = "android.view.View"
|
val VIEW_FQNAME = "android.view.View"
|
||||||
val ACTIVITY_FQNAME = "android.app.Activity"
|
val ACTIVITY_FQNAME = "android.app.Activity"
|
||||||
val FRAGMENT_FQNAME = "android.app.Fragment"
|
val FRAGMENT_FQNAME = "android.app.Fragment"
|
||||||
@@ -56,8 +58,6 @@ public object AndroidConst {
|
|||||||
val FQNAME_RESOLVE_PACKAGES = listOf("android.widget", "android.webkit", "android.view")
|
val FQNAME_RESOLVE_PACKAGES = listOf("android.widget", "android.webkit", "android.view")
|
||||||
}
|
}
|
||||||
|
|
||||||
public fun nameToIdDeclaration(name: String): String = AndroidConst.ID_DECLARATION_PREFIX + name
|
|
||||||
|
|
||||||
public fun idToName(id: String): String? {
|
public fun idToName(id: String): String? {
|
||||||
for (prefix in AndroidConst.XML_ID_PREFIXES) {
|
for (prefix in AndroidConst.XML_ID_PREFIXES) {
|
||||||
if (id.startsWith(prefix)) return escapeAndroidIdentifier(id.replace(prefix, ""))
|
if (id.startsWith(prefix)) return escapeAndroidIdentifier(id.replace(prefix, ""))
|
||||||
@@ -71,4 +71,13 @@ public fun isWidgetTypeIgnored(xmlType: String): Boolean {
|
|||||||
|
|
||||||
fun escapeAndroidIdentifier(id: String): String {
|
fun escapeAndroidIdentifier(id: String): String {
|
||||||
return if (id in AndroidConst.ESCAPED_IDENTIFIERS) "`$id`" else id
|
return if (id in AndroidConst.ESCAPED_IDENTIFIERS) "`$id`" else id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal fun <T> List<T>.forEachUntilLast(operation: (T) -> Unit) {
|
||||||
|
val lastIndex = lastIndex
|
||||||
|
forEachIndexed { i, t ->
|
||||||
|
if (i < lastIndex) {
|
||||||
|
operation(t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,85 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.android.synthetic
|
|
||||||
|
|
||||||
import java.util.ArrayList
|
|
||||||
|
|
||||||
open class Context(val buffer: StringBuffer = StringBuffer(), private var indentDepth: Int = 0) {
|
|
||||||
open class InvalidIndent(num: Int) : RuntimeException("Indentation level < 0: $num")
|
|
||||||
|
|
||||||
val indentUnit = " "
|
|
||||||
protected var currentIndent: String = indentUnit.repeat(indentDepth)
|
|
||||||
private val children = ArrayList<Context>()
|
|
||||||
|
|
||||||
public fun incIndent() {
|
|
||||||
indentDepth++
|
|
||||||
currentIndent += indentUnit
|
|
||||||
}
|
|
||||||
|
|
||||||
public fun decIndent() {
|
|
||||||
indentDepth--
|
|
||||||
if (indentDepth < 0)
|
|
||||||
throw InvalidIndent(indentDepth)
|
|
||||||
currentIndent = currentIndent.substring(0, currentIndent.length - indentUnit.length)
|
|
||||||
}
|
|
||||||
|
|
||||||
public open fun write(what: String) {
|
|
||||||
writeNoIndent(currentIndent)
|
|
||||||
writeNoIndent(what)
|
|
||||||
}
|
|
||||||
|
|
||||||
public fun writeNoIndent(what: String) {
|
|
||||||
buffer.append(what)
|
|
||||||
}
|
|
||||||
|
|
||||||
public fun writeln(what: String) {
|
|
||||||
write(what)
|
|
||||||
newLine()
|
|
||||||
}
|
|
||||||
|
|
||||||
public fun newLine() {
|
|
||||||
writeNoIndent("\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public fun trim(num: Int) {
|
|
||||||
buffer.delete(buffer.length - num, buffer.length())
|
|
||||||
}
|
|
||||||
|
|
||||||
public fun fork(newBuffer: StringBuffer = StringBuffer(),
|
|
||||||
newIndentDepth: Int = indentDepth): Context {
|
|
||||||
val child = Context(newBuffer, newIndentDepth)
|
|
||||||
children.add(child)
|
|
||||||
return child
|
|
||||||
}
|
|
||||||
|
|
||||||
public fun absorbChildren(noIndent: Boolean = true) {
|
|
||||||
for (child in children) {
|
|
||||||
child.absorbChildren()
|
|
||||||
if (noIndent)
|
|
||||||
writeNoIndent(child.toString())
|
|
||||||
else
|
|
||||||
write(child.toString())
|
|
||||||
}
|
|
||||||
children.clear()
|
|
||||||
}
|
|
||||||
|
|
||||||
public override fun toString(): String {
|
|
||||||
return buffer.toString()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
-85
@@ -1,85 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.android.synthetic
|
|
||||||
|
|
||||||
interface KotlinWriter {
|
|
||||||
fun toStringBuffer(): StringBuffer
|
|
||||||
}
|
|
||||||
|
|
||||||
class KotlinStringWriter : KotlinWriter {
|
|
||||||
|
|
||||||
private val ctx = Context()
|
|
||||||
private val imports = ctx.fork()
|
|
||||||
private val body = ctx.fork()
|
|
||||||
|
|
||||||
fun writeImmutableProperty(name: String,
|
|
||||||
retType: String,
|
|
||||||
getterBody: Collection<String>) {
|
|
||||||
body.writeln("val $name: $retType")
|
|
||||||
body.incIndent()
|
|
||||||
body.write("get() ")
|
|
||||||
if (getterBody.size > 1) {
|
|
||||||
body.writeNoIndent("{\n")
|
|
||||||
body.incIndent()
|
|
||||||
for (stmt in getterBody) {
|
|
||||||
body.writeln(stmt)
|
|
||||||
}
|
|
||||||
body.decIndent()
|
|
||||||
body.writeln("}")
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
body.writeNoIndent("=")
|
|
||||||
body.writeNoIndent(getterBody.joinToString("").replace("return", ""))
|
|
||||||
body.newLine()
|
|
||||||
}
|
|
||||||
body.decIndent()
|
|
||||||
body.newLine()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun writeImmutableExtensionProperty(receiver: String,
|
|
||||||
name: String,
|
|
||||||
retType: String,
|
|
||||||
getterBody: Collection<String>) {
|
|
||||||
writeImmutableProperty("$receiver.$name", retType, getterBody)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun writeImport(what: String) {
|
|
||||||
imports.writeln("import $what")
|
|
||||||
}
|
|
||||||
|
|
||||||
fun writePackage(_package: String) {
|
|
||||||
ctx.writeln("package $_package\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
fun writeEmptyLine() {
|
|
||||||
body.newLine()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun writeText(text: String) {
|
|
||||||
body.writeln(text)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun toStringBuffer(): StringBuffer {
|
|
||||||
ctx.absorbChildren()
|
|
||||||
return ctx.buffer
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun toString(): String {
|
|
||||||
return ctx.buffer.toString()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
+19
-19
@@ -17,6 +17,9 @@
|
|||||||
package org.jetbrains.kotlin.android.synthetic.codegen
|
package org.jetbrains.kotlin.android.synthetic.codegen
|
||||||
|
|
||||||
import org.jetbrains.kotlin.android.synthetic.AndroidConst
|
import org.jetbrains.kotlin.android.synthetic.AndroidConst
|
||||||
|
import org.jetbrains.kotlin.android.synthetic.descriptors.AndroidSyntheticPackageFragmentDescriptor
|
||||||
|
import org.jetbrains.kotlin.android.synthetic.res.AndroidSyntheticFunction
|
||||||
|
import org.jetbrains.kotlin.android.synthetic.res.AndroidSyntheticProperty
|
||||||
import org.jetbrains.kotlin.codegen.ClassBuilder
|
import org.jetbrains.kotlin.codegen.ClassBuilder
|
||||||
import org.jetbrains.kotlin.codegen.FunctionCodegen
|
import org.jetbrains.kotlin.codegen.FunctionCodegen
|
||||||
import org.jetbrains.kotlin.codegen.StackValue
|
import org.jetbrains.kotlin.codegen.StackValue
|
||||||
@@ -26,7 +29,6 @@ import org.jetbrains.kotlin.codegen.state.JetTypeMapper
|
|||||||
import org.jetbrains.kotlin.descriptors.*
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
import org.jetbrains.kotlin.load.java.lazy.descriptors.LazyJavaClassDescriptor
|
import org.jetbrains.kotlin.load.java.lazy.descriptors.LazyJavaClassDescriptor
|
||||||
import org.jetbrains.kotlin.psi.KtClassOrObject
|
import org.jetbrains.kotlin.psi.KtClassOrObject
|
||||||
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
|
|
||||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassOrAny
|
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassOrAny
|
||||||
@@ -109,12 +111,12 @@ public class AndroidExpressionCodegenExtension : ExpressionCodegenExtension {
|
|||||||
else null
|
else null
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun applyFunction(receiver: StackValue, resolvedCall: ResolvedCall<*>, c: ExpressionCodegenExtension.Context): Boolean {
|
override fun applyFunction(receiver: StackValue, resolvedCall: ResolvedCall<*>, c: ExpressionCodegenExtension.Context): StackValue? {
|
||||||
val resultingDescriptor = resolvedCall.resultingDescriptor
|
val resultingDescriptor = resolvedCall.resultingDescriptor
|
||||||
return if (resultingDescriptor is FunctionDescriptor) {
|
return if (resultingDescriptor is FunctionDescriptor) {
|
||||||
return generateSyntheticFunctionCall(receiver, resolvedCall, c, resultingDescriptor)
|
return generateSyntheticFunctionCall(receiver, resolvedCall, c, resultingDescriptor)
|
||||||
}
|
}
|
||||||
else false
|
else null
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun generateSyntheticFunctionCall(
|
private fun generateSyntheticFunctionCall(
|
||||||
@@ -122,22 +124,22 @@ public class AndroidExpressionCodegenExtension : ExpressionCodegenExtension {
|
|||||||
resolvedCall: ResolvedCall<*>,
|
resolvedCall: ResolvedCall<*>,
|
||||||
c: ExpressionCodegenExtension.Context,
|
c: ExpressionCodegenExtension.Context,
|
||||||
descriptor: FunctionDescriptor
|
descriptor: FunctionDescriptor
|
||||||
): Boolean {
|
): StackValue? {
|
||||||
if (descriptor.getAndroidPackage() == null) return false
|
if (descriptor !is AndroidSyntheticFunction) return null
|
||||||
if (descriptor.name.asString() != AndroidConst.CLEAR_FUNCTION_NAME) return false
|
if (descriptor.name.asString() != AndroidConst.CLEAR_FUNCTION_NAME) return null
|
||||||
|
|
||||||
val declarationDescriptor = resolvedCall.getReceiverDeclarationDescriptor() ?: return false
|
val declarationDescriptor = resolvedCall.getReceiverDeclarationDescriptor() ?: return null
|
||||||
if (!isCacheSupported(declarationDescriptor)) return true
|
if (!isCacheSupported(declarationDescriptor)) return StackValue.functionCall(Type.VOID_TYPE) {}
|
||||||
|
|
||||||
val androidClassType = AndroidClassType.getClassType(declarationDescriptor)
|
val androidClassType = AndroidClassType.getClassType(declarationDescriptor)
|
||||||
if (androidClassType == AndroidClassType.UNKNOWN) return false
|
if (androidClassType == AndroidClassType.UNKNOWN) return null
|
||||||
|
|
||||||
val bytecodeClassName = c.typeMapper.mapType(declarationDescriptor).internalName
|
return StackValue.functionCall(Type.VOID_TYPE) {
|
||||||
|
val bytecodeClassName = c.typeMapper.mapType(declarationDescriptor).internalName
|
||||||
|
|
||||||
receiver.put(c.typeMapper.mapType(declarationDescriptor), c.v)
|
receiver.put(c.typeMapper.mapType(declarationDescriptor), it)
|
||||||
c.v.invokevirtual(bytecodeClassName, CLEAR_CACHE_METHOD_NAME, "()V", false)
|
it.invokevirtual(bytecodeClassName, CLEAR_CACHE_METHOD_NAME, "()V", false)
|
||||||
|
}
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun generateSyntheticPropertyCall(
|
private fun generateSyntheticPropertyCall(
|
||||||
@@ -146,7 +148,9 @@ public class AndroidExpressionCodegenExtension : ExpressionCodegenExtension {
|
|||||||
c: ExpressionCodegenExtension.Context,
|
c: ExpressionCodegenExtension.Context,
|
||||||
descriptor: PropertyDescriptor
|
descriptor: PropertyDescriptor
|
||||||
): StackValue? {
|
): StackValue? {
|
||||||
val androidPackage = descriptor.getAndroidPackage() ?: return null
|
if (descriptor !is AndroidSyntheticProperty) return null
|
||||||
|
val packageFragment = descriptor.containingDeclaration as? AndroidSyntheticPackageFragmentDescriptor ?: return null
|
||||||
|
val androidPackage = packageFragment.packageData.moduleData.module.applicationPackage
|
||||||
val declarationDescriptor = resolvedCall.getReceiverDeclarationDescriptor() ?: return null
|
val declarationDescriptor = resolvedCall.getReceiverDeclarationDescriptor() ?: return null
|
||||||
val androidClassType = AndroidClassType.getClassType(declarationDescriptor)
|
val androidClassType = AndroidClassType.getClassType(declarationDescriptor)
|
||||||
|
|
||||||
@@ -224,10 +228,6 @@ public class AndroidExpressionCodegenExtension : ExpressionCodegenExtension {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun CallableDescriptor.getAndroidPackage(): String? {
|
|
||||||
return DescriptorToSourceUtils.getContainingFile(this)?.getUserData(AndroidConst.ANDROID_USER_PACKAGE)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun ResolvedCall<*>.getReceiverDeclarationDescriptor(): ClassifierDescriptor? {
|
private fun ResolvedCall<*>.getReceiverDeclarationDescriptor(): ClassifierDescriptor? {
|
||||||
return (extensionReceiver as ReceiverValue).type.constructor.declarationDescriptor
|
return (extensionReceiver as ReceiverValue).type.constructor.declarationDescriptor
|
||||||
}
|
}
|
||||||
|
|||||||
+87
@@ -0,0 +1,87 @@
|
|||||||
|
/*
|
||||||
|
* 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.android.synthetic.descriptors
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.android.synthetic.res.*
|
||||||
|
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||||
|
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||||
|
import org.jetbrains.kotlin.descriptors.impl.PackageFragmentDescriptorImpl
|
||||||
|
import org.jetbrains.kotlin.incremental.components.LookupLocation
|
||||||
|
import org.jetbrains.kotlin.name.FqName
|
||||||
|
import org.jetbrains.kotlin.name.Name
|
||||||
|
import org.jetbrains.kotlin.resolve.descriptorUtil.module
|
||||||
|
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
|
||||||
|
import org.jetbrains.kotlin.resolve.scopes.MemberScope
|
||||||
|
import org.jetbrains.kotlin.resolve.scopes.MemberScopeImpl
|
||||||
|
import org.jetbrains.kotlin.storage.StorageManager
|
||||||
|
import org.jetbrains.kotlin.utils.Printer
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
class AndroidSyntheticPackageData(
|
||||||
|
val moduleData: AndroidModuleData,
|
||||||
|
val forView: Boolean,
|
||||||
|
val isDeprecated: Boolean,
|
||||||
|
val resources: List<AndroidResource>)
|
||||||
|
|
||||||
|
class AndroidSyntheticPackageFragmentDescriptor(
|
||||||
|
module: ModuleDescriptor,
|
||||||
|
fqName: FqName,
|
||||||
|
val packageData: AndroidSyntheticPackageData,
|
||||||
|
private val lazyContext: LazySyntheticElementResolveContext,
|
||||||
|
private val storageManager: StorageManager
|
||||||
|
) : PackageFragmentDescriptorImpl(module, fqName) {
|
||||||
|
private val scope = AndroidExtensionPropertiesScope()
|
||||||
|
override fun getMemberScope(): MemberScope = scope
|
||||||
|
|
||||||
|
private inner class AndroidExtensionPropertiesScope : MemberScopeImpl() {
|
||||||
|
private val properties = storageManager.createLazyValue {
|
||||||
|
val packageFragmentDescriptor = this@AndroidSyntheticPackageFragmentDescriptor
|
||||||
|
|
||||||
|
val context = lazyContext()
|
||||||
|
val widgetReceivers = context.getWidgetReceivers(packageData.forView)
|
||||||
|
val fragmentTypes = context.fragmentTypes
|
||||||
|
|
||||||
|
val properties = ArrayList<PropertyDescriptor>(0)
|
||||||
|
for (resource in packageData.resources) {
|
||||||
|
when (resource) {
|
||||||
|
is AndroidResource.Widget -> {
|
||||||
|
val resolvedWidget = resource.resolve(module)
|
||||||
|
for (receiver in widgetReceivers) {
|
||||||
|
properties += genPropertyForWidget(packageFragmentDescriptor, receiver, resolvedWidget, context)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is AndroidResource.Fragment -> if (!packageData.forView) {
|
||||||
|
for ((receiverType, type) in fragmentTypes) {
|
||||||
|
properties += genPropertyForFragment(packageFragmentDescriptor, receiverType, type, resource)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
properties
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getContributedDescriptors(kindFilter: DescriptorKindFilter, nameFilter: (Name) -> Boolean) =
|
||||||
|
properties().filter { kindFilter.acceptsKinds(DescriptorKindFilter.VARIABLES_MASK) && nameFilter(it.name) }
|
||||||
|
|
||||||
|
override fun getContributedVariables(name: Name, location: LookupLocation) = properties().filter { it.name == name }
|
||||||
|
|
||||||
|
override fun printScopeStructure(p: Printer) {
|
||||||
|
p.println(javaClass.simpleName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+94
@@ -0,0 +1,94 @@
|
|||||||
|
/*
|
||||||
|
* 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.android.synthetic.descriptors
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.android.synthetic.AndroidConst
|
||||||
|
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||||
|
import org.jetbrains.kotlin.name.ClassId
|
||||||
|
import org.jetbrains.kotlin.name.FqName
|
||||||
|
import org.jetbrains.kotlin.resolve.descriptorUtil.module
|
||||||
|
import org.jetbrains.kotlin.serialization.deserialization.findClassAcrossModuleDependencies
|
||||||
|
import org.jetbrains.kotlin.storage.StorageManager
|
||||||
|
import org.jetbrains.kotlin.types.ErrorUtils
|
||||||
|
import org.jetbrains.kotlin.types.KotlinType
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
class LazySyntheticElementResolveContext(private val module: ModuleDescriptor, private val storageManager: StorageManager) {
|
||||||
|
private val context = storageManager.createLazyValue {
|
||||||
|
module.createResolveContext()
|
||||||
|
}
|
||||||
|
|
||||||
|
internal operator fun invoke() = context()
|
||||||
|
|
||||||
|
private fun ModuleDescriptor.createResolveContext(): SyntheticElementResolveContext {
|
||||||
|
fun find(fqName: String) = module.findClassAcrossModuleDependencies(ClassId.topLevel(FqName(fqName)))
|
||||||
|
|
||||||
|
val viewDescriptor = find(AndroidConst.VIEW_FQNAME) ?: return SyntheticElementResolveContext.ERROR_CONTEXT
|
||||||
|
val activityDescriptor = find(AndroidConst.ACTIVITY_FQNAME) ?: return SyntheticElementResolveContext.ERROR_CONTEXT
|
||||||
|
val fragmentDescriptor = find(AndroidConst.FRAGMENT_FQNAME)
|
||||||
|
val supportActivityDescriptor = find(AndroidConst.SUPPORT_FRAGMENT_ACTIVITY_FQNAME)
|
||||||
|
val supportFragmentDescriptor = find(AndroidConst.SUPPORT_FRAGMENT_FQNAME)
|
||||||
|
|
||||||
|
return SyntheticElementResolveContext(
|
||||||
|
viewDescriptor.defaultType,
|
||||||
|
activityDescriptor.defaultType,
|
||||||
|
fragmentDescriptor?.defaultType,
|
||||||
|
supportActivityDescriptor?.defaultType,
|
||||||
|
supportFragmentDescriptor?.defaultType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class SyntheticElementResolveContext(
|
||||||
|
val viewType: KotlinType,
|
||||||
|
val activityType: KotlinType,
|
||||||
|
val fragmentType: KotlinType?,
|
||||||
|
val supportActivityType: KotlinType?,
|
||||||
|
val supportFragmentType: KotlinType?) {
|
||||||
|
companion object {
|
||||||
|
private fun errorType() = ErrorUtils.createErrorType("")
|
||||||
|
val ERROR_CONTEXT = SyntheticElementResolveContext(errorType(), errorType(), null, null, null)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val widgetReceivers by lazy {
|
||||||
|
val receivers = ArrayList<KotlinType>(3)
|
||||||
|
receivers += activityType
|
||||||
|
fragmentType?.let { receivers += it }
|
||||||
|
supportFragmentType?.let { receivers += it }
|
||||||
|
receivers
|
||||||
|
}
|
||||||
|
|
||||||
|
val fragmentTypes: List<Pair<KotlinType, KotlinType>> by lazy {
|
||||||
|
if (fragmentType == null) {
|
||||||
|
emptyList<Pair<KotlinType, KotlinType>>()
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
val types = ArrayList<Pair<KotlinType, KotlinType>>(4)
|
||||||
|
types += Pair(activityType, fragmentType)
|
||||||
|
types += Pair(fragmentType, fragmentType)
|
||||||
|
if (supportActivityType != null && supportFragmentType != null) {
|
||||||
|
types += Pair(supportFragmentType, supportFragmentType)
|
||||||
|
types += Pair(supportActivityType, supportFragmentType)
|
||||||
|
}
|
||||||
|
types
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getWidgetReceivers(forView: Boolean): List<KotlinType> {
|
||||||
|
if (forView) return listOf(viewType)
|
||||||
|
return widgetReceivers
|
||||||
|
}
|
||||||
|
}
|
||||||
+61
@@ -0,0 +1,61 @@
|
|||||||
|
/*
|
||||||
|
* 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.android.synthetic.descriptors
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||||
|
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||||
|
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||||
|
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||||
|
import org.jetbrains.kotlin.descriptors.impl.PackageFragmentDescriptorImpl
|
||||||
|
import org.jetbrains.kotlin.incremental.components.LookupLocation
|
||||||
|
import org.jetbrains.kotlin.name.FqName
|
||||||
|
import org.jetbrains.kotlin.name.Name
|
||||||
|
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
|
||||||
|
import org.jetbrains.kotlin.resolve.scopes.MemberScopeImpl
|
||||||
|
import org.jetbrains.kotlin.storage.StorageManager
|
||||||
|
import org.jetbrains.kotlin.utils.Printer
|
||||||
|
|
||||||
|
class PredefinedPackageFragmentDescriptor(
|
||||||
|
fqName: String,
|
||||||
|
module: ModuleDescriptor,
|
||||||
|
private val storageManager: StorageManager,
|
||||||
|
private val descriptors: (PredefinedPackageFragmentDescriptor) -> Collection<DeclarationDescriptor> = { emptyList() }
|
||||||
|
) : PackageFragmentDescriptorImpl(module, FqName(fqName)) {
|
||||||
|
private val calculatedDescriptors = storageManager.createLazyValue {
|
||||||
|
descriptors(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val scope = PredefinedScope()
|
||||||
|
override fun getMemberScope() = scope
|
||||||
|
|
||||||
|
inner class PredefinedScope : MemberScopeImpl() {
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
override fun getContributedVariables(name: Name, location: LookupLocation) =
|
||||||
|
calculatedDescriptors().filter { it is PropertyDescriptor && it.name == name } as List<PropertyDescriptor>
|
||||||
|
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
override fun getContributedFunctions(name: Name, location: LookupLocation) =
|
||||||
|
calculatedDescriptors().filter { it is FunctionDescriptor && it.name == name } as List<FunctionDescriptor>
|
||||||
|
|
||||||
|
override fun getContributedDescriptors(kindFilter: DescriptorKindFilter, nameFilter: (Name) -> Boolean) =
|
||||||
|
calculatedDescriptors().filter { nameFilter(it.name) && kindFilter.accepts(it) }
|
||||||
|
|
||||||
|
override fun printScopeStructure(p: Printer) {
|
||||||
|
p.println(javaClass.simpleName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+25
-15
@@ -16,32 +16,42 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.android.synthetic.diagnostic
|
package org.jetbrains.kotlin.android.synthetic.diagnostic
|
||||||
|
|
||||||
import org.jetbrains.kotlin.android.synthetic.AndroidConst
|
|
||||||
import org.jetbrains.kotlin.android.synthetic.res.SyntheticFileGenerator
|
|
||||||
import org.jetbrains.kotlin.descriptors.*
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
import org.jetbrains.kotlin.resolve.calls.checkers.CallChecker
|
import org.jetbrains.kotlin.resolve.calls.checkers.CallChecker
|
||||||
import org.jetbrains.kotlin.resolve.calls.context.BasicCallResolutionContext
|
import org.jetbrains.kotlin.resolve.calls.context.BasicCallResolutionContext
|
||||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||||
import org.jetbrains.kotlin.resolve.constants.StringValue
|
|
||||||
import org.jetbrains.kotlin.android.synthetic.diagnostic.ErrorsAndroid.*
|
import org.jetbrains.kotlin.android.synthetic.diagnostic.ErrorsAndroid.*
|
||||||
|
import org.jetbrains.kotlin.android.synthetic.descriptors.AndroidSyntheticPackageFragmentDescriptor
|
||||||
|
import org.jetbrains.kotlin.android.synthetic.res.AndroidSyntheticProperty
|
||||||
|
import org.jetbrains.kotlin.diagnostics.DiagnosticSink
|
||||||
|
import org.jetbrains.kotlin.psi.KtExpression
|
||||||
|
|
||||||
public class AndroidExtensionPropertiesCallChecker : CallChecker {
|
public class AndroidExtensionPropertiesCallChecker : CallChecker {
|
||||||
override fun <F : CallableDescriptor> check(resolvedCall: ResolvedCall<F>, context: BasicCallResolutionContext) {
|
override fun <F : CallableDescriptor> check(resolvedCall: ResolvedCall<F>, context: BasicCallResolutionContext) {
|
||||||
val expression = context.call.calleeExpression ?: return
|
val expression = context.call.calleeExpression ?: return
|
||||||
|
|
||||||
val propertyDescriptor = resolvedCall.resultingDescriptor as? PropertyDescriptor ?: return
|
val propertyDescriptor = resolvedCall.resultingDescriptor as? PropertyDescriptor ?: return
|
||||||
val syntheticPackage = propertyDescriptor.containingDeclaration as? PackageFragmentDescriptor ?: return
|
val containingPackage = propertyDescriptor.containingDeclaration as? AndroidSyntheticPackageFragmentDescriptor ?: return
|
||||||
if (!syntheticPackage.fqName.asString().startsWith("${AndroidConst.SYNTHETIC_PACKAGE}.")) return
|
val androidSyntheticProperty = propertyDescriptor as? AndroidSyntheticProperty ?: return
|
||||||
|
|
||||||
val invalidWidgetTypeAnnotation = propertyDescriptor.annotations.findAnnotation(
|
with (context.trace) {
|
||||||
SyntheticFileGenerator.INVALID_WIDGET_TYPE_ANNOTATION_FQNAME) ?: return
|
checkUnresolvedWidgetType(expression, androidSyntheticProperty)
|
||||||
|
checkDeprecated(expression, containingPackage)
|
||||||
val type = invalidWidgetTypeAnnotation.allValueArguments.filterKeys {
|
}
|
||||||
it.name.asString() == SyntheticFileGenerator.INVALID_WIDGET_TYPE_ANNOTATION_TYPE_PARAMETER
|
|
||||||
}.values().firstOrNull() as? StringValue ?: return
|
|
||||||
|
|
||||||
val erroneousType = type.value
|
|
||||||
val warning = if (erroneousType.contains('.')) SYNTHETIC_UNRESOLVED_WIDGET_TYPE else SYNTHETIC_INVALID_WIDGET_TYPE
|
|
||||||
context.trace.report(warning.on(expression, erroneousType))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun DiagnosticSink.checkDeprecated(expression: KtExpression, packageDescriptor: AndroidSyntheticPackageFragmentDescriptor) {
|
||||||
|
if (packageDescriptor.packageData.isDeprecated) {
|
||||||
|
report(SYNTHETIC_DEPRECATED_PACKAGE.on(expression))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun DiagnosticSink.checkUnresolvedWidgetType(expression: KtExpression, property: AndroidSyntheticProperty) {
|
||||||
|
if (!property.isErrorType) return
|
||||||
|
val type = property.errorType ?: return
|
||||||
|
|
||||||
|
val warning = if (type.contains('.')) SYNTHETIC_UNRESOLVED_WIDGET_TYPE else SYNTHETIC_INVALID_WIDGET_TYPE
|
||||||
|
report(warning.on(expression, type))
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
+3
@@ -33,6 +33,9 @@ public class DefaultErrorMessagesAndroid : DefaultErrorMessages.Extension {
|
|||||||
MAP.put(ErrorsAndroid.SYNTHETIC_UNRESOLVED_WIDGET_TYPE,
|
MAP.put(ErrorsAndroid.SYNTHETIC_UNRESOLVED_WIDGET_TYPE,
|
||||||
"Widget has an unresolved type ''{0}'', and thus it was upcasted to ''android.view.View''",
|
"Widget has an unresolved type ''{0}'', and thus it was upcasted to ''android.view.View''",
|
||||||
Renderers.TO_STRING)
|
Renderers.TO_STRING)
|
||||||
|
|
||||||
|
MAP.put(ErrorsAndroid.SYNTHETIC_DEPRECATED_PACKAGE,
|
||||||
|
"Use properties from the build variant packages")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.android.synthetic.diagnostic;
|
package org.jetbrains.kotlin.android.synthetic.diagnostic;
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory0;
|
||||||
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory1;
|
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory1;
|
||||||
import org.jetbrains.kotlin.diagnostics.Errors;
|
import org.jetbrains.kotlin.diagnostics.Errors;
|
||||||
import org.jetbrains.kotlin.psi.KtExpression;
|
import org.jetbrains.kotlin.psi.KtExpression;
|
||||||
@@ -25,6 +26,7 @@ import static org.jetbrains.kotlin.diagnostics.Severity.WARNING;
|
|||||||
public interface ErrorsAndroid {
|
public interface ErrorsAndroid {
|
||||||
DiagnosticFactory1<KtExpression, String> SYNTHETIC_INVALID_WIDGET_TYPE = DiagnosticFactory1.create(WARNING);
|
DiagnosticFactory1<KtExpression, String> SYNTHETIC_INVALID_WIDGET_TYPE = DiagnosticFactory1.create(WARNING);
|
||||||
DiagnosticFactory1<KtExpression, String> SYNTHETIC_UNRESOLVED_WIDGET_TYPE = DiagnosticFactory1.create(WARNING);
|
DiagnosticFactory1<KtExpression, String> SYNTHETIC_UNRESOLVED_WIDGET_TYPE = DiagnosticFactory1.create(WARNING);
|
||||||
|
DiagnosticFactory0<KtExpression> SYNTHETIC_DEPRECATED_PACKAGE = DiagnosticFactory0.create(WARNING);
|
||||||
|
|
||||||
@SuppressWarnings("UnusedDeclaration")
|
@SuppressWarnings("UnusedDeclaration")
|
||||||
Object _initializer = new Object() {
|
Object _initializer = new Object() {
|
||||||
|
|||||||
+44
-2
@@ -24,6 +24,9 @@ import com.intellij.openapi.vfs.VirtualFileManager
|
|||||||
import com.intellij.psi.PsiElement
|
import com.intellij.psi.PsiElement
|
||||||
import com.intellij.psi.PsiFile
|
import com.intellij.psi.PsiFile
|
||||||
import com.intellij.psi.PsiManager
|
import com.intellij.psi.PsiManager
|
||||||
|
import org.jetbrains.kotlin.android.synthetic.AndroidConst
|
||||||
|
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||||
|
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||||
import org.jetbrains.kotlin.psi.KtProperty
|
import org.jetbrains.kotlin.psi.KtProperty
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
@@ -38,9 +41,9 @@ abstract class AndroidLayoutXmlFileManager(val project: Project) {
|
|||||||
|
|
||||||
public abstract val androidModule: AndroidModule?
|
public abstract val androidModule: AndroidModule?
|
||||||
|
|
||||||
public open fun propertyToXmlAttributes(property: KtProperty): List<PsiElement> = listOf()
|
public open fun propertyToXmlAttributes(propertyDescriptor: PropertyDescriptor): List<PsiElement> = listOf()
|
||||||
|
|
||||||
public fun getLayoutXmlFiles(): AndroidModuleData {
|
open fun getModuleData(): AndroidModuleData {
|
||||||
val androidModule = androidModule ?: return AndroidModuleData.EMPTY
|
val androidModule = androidModule ?: return AndroidModuleData.EMPTY
|
||||||
return AndroidModuleData(androidModule, androidModule.variants.map { getVariantData(it) })
|
return AndroidModuleData(androidModule, androidModule.variants.map { getVariantData(it) })
|
||||||
}
|
}
|
||||||
@@ -82,6 +85,45 @@ abstract class AndroidLayoutXmlFileManager(val project: Project) {
|
|||||||
return AndroidVariantData(variant, layoutNameToXmlFiles)
|
return AndroidVariantData(variant, layoutNameToXmlFiles)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun extractResources(files: List<PsiFile>, module: ModuleDescriptor): List<AndroidResource> {
|
||||||
|
return filterDuplicates(doExtractResources(files, module))
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract fun doExtractResources(files: List<PsiFile>, module: ModuleDescriptor): List<AndroidResource>
|
||||||
|
|
||||||
|
protected fun parseAndroidResource(id: String, tag: String, sourceElement: PsiElement?): AndroidResource {
|
||||||
|
return when (tag) {
|
||||||
|
"fragment" -> AndroidResource.Fragment(id, sourceElement)
|
||||||
|
"include" -> AndroidResource.Widget(id, AndroidConst.VIEW_FQNAME, sourceElement)
|
||||||
|
else -> AndroidResource.Widget(id, tag, sourceElement)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun filterDuplicates(resources: List<AndroidResource>): List<AndroidResource> {
|
||||||
|
val resourceMap = linkedMapOf<String, AndroidResource>()
|
||||||
|
val resourcesToExclude = hashSetOf<String>()
|
||||||
|
|
||||||
|
for (res in resources) {
|
||||||
|
if (resourceMap.contains(res.id)) {
|
||||||
|
val existing = resourceMap[res.id]!!
|
||||||
|
|
||||||
|
if (!res.sameClass(existing)) {
|
||||||
|
resourcesToExclude.add(res.id)
|
||||||
|
}
|
||||||
|
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) {
|
||||||
|
resourceMap.put(res.id, AndroidResource.Widget(res.id, AndroidConst.VIEW_FQNAME, res.sourceElement))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else resourceMap.put(res.id, res)
|
||||||
|
}
|
||||||
|
resourcesToExclude.forEach { resourceMap.remove(it) }
|
||||||
|
return resourceMap.values.toList()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
public fun getInstance(module: Module): AndroidLayoutXmlFileManager? {
|
public fun getInstance(module: Module): AndroidLayoutXmlFileManager? {
|
||||||
val service = ModuleServiceManager.getService(module, AndroidLayoutXmlFileManager::class.java)
|
val service = ModuleServiceManager.getService(module, AndroidLayoutXmlFileManager::class.java)
|
||||||
|
|||||||
+106
@@ -0,0 +1,106 @@
|
|||||||
|
/*
|
||||||
|
* 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.android.synthetic.res
|
||||||
|
|
||||||
|
import com.intellij.openapi.project.Project
|
||||||
|
import org.jetbrains.kotlin.analyzer.ModuleInfo
|
||||||
|
import org.jetbrains.kotlin.android.synthetic.AndroidConst
|
||||||
|
import org.jetbrains.kotlin.android.synthetic.descriptors.*
|
||||||
|
import org.jetbrains.kotlin.android.synthetic.forEachUntilLast
|
||||||
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
|
import org.jetbrains.kotlin.name.FqName
|
||||||
|
import org.jetbrains.kotlin.name.Name
|
||||||
|
import org.jetbrains.kotlin.resolve.BindingTrace
|
||||||
|
import org.jetbrains.kotlin.resolve.jvm.extensions.PackageFragmentProviderExtension
|
||||||
|
import org.jetbrains.kotlin.storage.StorageManager
|
||||||
|
|
||||||
|
abstract class AndroidPackageFragmentProviderExtension : PackageFragmentProviderExtension {
|
||||||
|
protected abstract fun getLayoutXmlFileManager(project: Project, moduleInfo: ModuleInfo?): AndroidLayoutXmlFileManager?
|
||||||
|
|
||||||
|
private val cache = WeakHashMap<AndroidModuleData, AndroidSyntheticPackageFragmentProvider>()
|
||||||
|
|
||||||
|
override fun getPackageFragmentProvider(
|
||||||
|
project: Project,
|
||||||
|
module: ModuleDescriptor,
|
||||||
|
storageManager: StorageManager,
|
||||||
|
trace: BindingTrace,
|
||||||
|
moduleInfo: ModuleInfo?
|
||||||
|
): PackageFragmentProvider? {
|
||||||
|
val layoutXmlFileManager = getLayoutXmlFileManager(project, moduleInfo) ?: return null
|
||||||
|
|
||||||
|
val moduleData = layoutXmlFileManager.getModuleData()
|
||||||
|
|
||||||
|
val cachedPackageFragmentProvider = cache[moduleData]
|
||||||
|
if (cachedPackageFragmentProvider != null) return cachedPackageFragmentProvider
|
||||||
|
|
||||||
|
val lazyContext = LazySyntheticElementResolveContext(module, storageManager)
|
||||||
|
|
||||||
|
val packageDescriptors = arrayListOf<PackageFragmentDescriptor>()
|
||||||
|
|
||||||
|
// Packages with synthetic properties
|
||||||
|
for (variantData in moduleData) {
|
||||||
|
for ((layoutName, layouts) in variantData) {
|
||||||
|
fun createPackageFragment(fqName: String, forView: Boolean, isDeprecated: Boolean = false): PackageFragmentDescriptor {
|
||||||
|
val resources = layoutXmlFileManager.extractResources(layouts, module)
|
||||||
|
val packageData = AndroidSyntheticPackageData(moduleData, forView, isDeprecated, resources)
|
||||||
|
return AndroidSyntheticPackageFragmentDescriptor(module, FqName(fqName), packageData, lazyContext, storageManager)
|
||||||
|
}
|
||||||
|
|
||||||
|
val packageFqName = AndroidConst.SYNTHETIC_PACKAGE + '.' + variantData.variant.name + '.' + layoutName
|
||||||
|
|
||||||
|
packageDescriptors += createPackageFragment(packageFqName, false)
|
||||||
|
packageDescriptors += createPackageFragment(packageFqName + ".view", true)
|
||||||
|
|
||||||
|
if (variantData.variant.isMainVariant && !moduleData.any { it.variant.name == layoutName }) {
|
||||||
|
val deprecatedPackageFqName = AndroidConst.SYNTHETIC_PACKAGE + '.' + layoutName
|
||||||
|
packageDescriptors += createPackageFragment(deprecatedPackageFqName, false, isDeprecated = true)
|
||||||
|
packageDescriptors += createPackageFragment(deprecatedPackageFqName + ".view", true, isDeprecated = true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Empty middle packages
|
||||||
|
AndroidConst.SYNTHETIC_SUBPACKAGES.forEachUntilLast { s ->
|
||||||
|
packageDescriptors += PredefinedPackageFragmentDescriptor(s, module, storageManager)
|
||||||
|
}
|
||||||
|
for (variantData in moduleData) {
|
||||||
|
val fqName = AndroidConst.SYNTHETIC_PACKAGE + '.' + variantData.variant.name
|
||||||
|
packageDescriptors += PredefinedPackageFragmentDescriptor(fqName, module, storageManager)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Package with clearFindViewByIdCache()
|
||||||
|
AndroidConst.SYNTHETIC_SUBPACKAGES.last().let { s ->
|
||||||
|
packageDescriptors += PredefinedPackageFragmentDescriptor(s, module, storageManager) { descriptor ->
|
||||||
|
lazyContext().getWidgetReceivers(false).map { genClearCacheFunction(descriptor, it) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val provider = AndroidSyntheticPackageFragmentProvider(allPackageDescriptors)
|
||||||
|
cache[moduleData] = provider
|
||||||
|
return provider
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class AndroidSyntheticPackageFragmentProvider(val packageFragments: Collection<PackageFragmentDescriptor>) : 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()
|
||||||
|
}
|
||||||
+29
-2
@@ -16,7 +16,12 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.android.synthetic.res
|
package org.jetbrains.kotlin.android.synthetic.res
|
||||||
|
|
||||||
|
import com.intellij.openapi.diagnostic.Logger
|
||||||
import com.intellij.openapi.project.Project
|
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
|
||||||
import javax.xml.parsers.SAXParser
|
import javax.xml.parsers.SAXParser
|
||||||
import javax.xml.parsers.SAXParserFactory
|
import javax.xml.parsers.SAXParserFactory
|
||||||
|
|
||||||
@@ -25,10 +30,32 @@ public class CliAndroidLayoutXmlFileManager(
|
|||||||
private val applicationPackage: String,
|
private val applicationPackage: String,
|
||||||
private val variants: List<AndroidVariant>
|
private val variants: List<AndroidVariant>
|
||||||
) : AndroidLayoutXmlFileManager(project) {
|
) : AndroidLayoutXmlFileManager(project) {
|
||||||
|
private companion object {
|
||||||
|
val LOG = Logger.getInstance(CliAndroidLayoutXmlFileManager::class.java)
|
||||||
|
}
|
||||||
|
|
||||||
override val androidModule by lazy { AndroidModule(applicationPackage, variants) }
|
override val androidModule = AndroidModule(applicationPackage, variants)
|
||||||
|
|
||||||
val saxParser: SAXParser = initSAX()
|
private val saxParser: SAXParser = initSAX()
|
||||||
|
|
||||||
|
override fun doExtractResources(files: List<PsiFile>, module: ModuleDescriptor): List<AndroidResource> {
|
||||||
|
val resources = arrayListOf<AndroidResource>()
|
||||||
|
|
||||||
|
val handler = AndroidXmlHandler { id, tag ->
|
||||||
|
resources += parseAndroidResource(id, tag, null)
|
||||||
|
}
|
||||||
|
|
||||||
|
for (file in files) {
|
||||||
|
try {
|
||||||
|
val inputStream = ByteArrayInputStream(file.virtualFile.contentsToByteArray())
|
||||||
|
saxParser.parse(inputStream, handler)
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
LOG.error(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return resources
|
||||||
|
}
|
||||||
|
|
||||||
protected fun initSAX(): SAXParser {
|
protected fun initSAX(): SAXParser {
|
||||||
val saxFactory = SAXParserFactory.newInstance()
|
val saxFactory = SAXParserFactory.newInstance()
|
||||||
|
|||||||
+9
-14
@@ -14,19 +14,14 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.jetbrains.kotlin.android.synthetic
|
package org.jetbrains.kotlin.android.synthetic.res
|
||||||
|
|
||||||
import com.intellij.openapi.application.ApplicationManager
|
import com.intellij.openapi.components.ServiceManager
|
||||||
import com.intellij.openapi.util.Computable
|
import com.intellij.openapi.project.Project
|
||||||
import com.intellij.psi.PsiElement
|
import org.jetbrains.kotlin.analyzer.ModuleInfo
|
||||||
import com.intellij.psi.PsiFile
|
|
||||||
|
|
||||||
fun isAndroidSyntheticFile(f: PsiFile?): Boolean {
|
class CliAndroidPackageFragmentProviderExtension : AndroidPackageFragmentProviderExtension() {
|
||||||
return f?.getUserData(AndroidConst.ANDROID_USER_PACKAGE) != null
|
override fun getLayoutXmlFileManager(project: Project, moduleInfo: ModuleInfo?): AndroidLayoutXmlFileManager? {
|
||||||
}
|
return ServiceManager.getService(project, AndroidLayoutXmlFileManager::class.java)
|
||||||
|
}
|
||||||
public fun isAndroidSyntheticElement(element: PsiElement?): Boolean {
|
}
|
||||||
return isAndroidSyntheticFile(ApplicationManager.getApplication().runReadAction(Computable {
|
|
||||||
element?.containingFile
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
-91
@@ -1,91 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.android.synthetic.res
|
|
||||||
|
|
||||||
import com.intellij.openapi.diagnostic.Logger
|
|
||||||
import com.intellij.openapi.project.Project
|
|
||||||
import com.intellij.psi.PsiElementFinder
|
|
||||||
import com.intellij.psi.PsiFile
|
|
||||||
import com.intellij.psi.impl.PsiElementFinderImpl
|
|
||||||
import com.intellij.psi.search.GlobalSearchScope
|
|
||||||
import org.jetbrains.kotlin.android.synthetic.AndroidConst
|
|
||||||
import org.jetbrains.kotlin.android.synthetic.AndroidXmlHandler
|
|
||||||
import org.jetbrains.kotlin.psi.KtFile
|
|
||||||
import java.io.ByteArrayInputStream
|
|
||||||
|
|
||||||
public open class CliSyntheticFileGenerator(
|
|
||||||
project: Project,
|
|
||||||
private val manifestPath: String,
|
|
||||||
private val variants: List<AndroidVariant>
|
|
||||||
) : SyntheticFileGenerator(project) {
|
|
||||||
|
|
||||||
private val cachedJetFiles by lazy {
|
|
||||||
val supportV4 = supportV4Available()
|
|
||||||
|
|
||||||
generateSyntheticJetFiles(generateSyntheticFiles(true, supportV4))
|
|
||||||
}
|
|
||||||
|
|
||||||
override val layoutXmlFileManager: CliAndroidLayoutXmlFileManager by lazy {
|
|
||||||
CliAndroidLayoutXmlFileManager(project, manifestPath, variants)
|
|
||||||
}
|
|
||||||
|
|
||||||
public override fun getSyntheticFiles(): List<KtFile> = cachedJetFiles
|
|
||||||
|
|
||||||
override fun extractLayoutResources(files: List<PsiFile>): List<AndroidResource> {
|
|
||||||
val resources = arrayListOf<AndroidResource>()
|
|
||||||
|
|
||||||
val handler = AndroidXmlHandler { id, tag ->
|
|
||||||
resources += parseAndroidResource(id, tag) { tag ->
|
|
||||||
resolveFqClassNameForView(tag)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (file in files) {
|
|
||||||
try {
|
|
||||||
val inputStream = ByteArrayInputStream(file.virtualFile.contentsToByteArray())
|
|
||||||
layoutXmlFileManager.saxParser.parse(inputStream, handler)
|
|
||||||
} catch (e: Throwable) {
|
|
||||||
LOG.error(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return filterDuplicates(resources)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun checkIfClassExist(fqName: String): Boolean {
|
|
||||||
val scope = GlobalSearchScope.allScope(project)
|
|
||||||
val psiElementFinders = project.getExtensions(PsiElementFinder.EP_NAME).filter { it is PsiElementFinderImpl }
|
|
||||||
|
|
||||||
for (finder in psiElementFinders) {
|
|
||||||
val clazz = finder.findClass(fqName, scope)
|
|
||||||
if (clazz != null) return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun parseAndroidWidget(id: String, tag: String, fqNameResolver: (String) -> String?): AndroidResource {
|
|
||||||
val fqName = fqNameResolver(tag)
|
|
||||||
val invalidType = if (fqName != null) null else tag
|
|
||||||
val type = fqName ?: (if ('.' in tag) tag else AndroidConst.VIEW_FQNAME)
|
|
||||||
return AndroidWidget(id, type, invalidType)
|
|
||||||
}
|
|
||||||
|
|
||||||
private companion object {
|
|
||||||
private val LOG: Logger = Logger.getInstance(CliSyntheticFileGenerator::class.java)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
-258
@@ -1,258 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.android.synthetic.res
|
|
||||||
|
|
||||||
import com.intellij.openapi.project.Project
|
|
||||||
import com.intellij.psi.PsiFile
|
|
||||||
import com.intellij.psi.PsiManager
|
|
||||||
import com.intellij.psi.util.CachedValue
|
|
||||||
import com.intellij.psi.util.CachedValueProvider.Result
|
|
||||||
import com.intellij.psi.util.CachedValuesManager
|
|
||||||
import com.intellij.testFramework.LightVirtualFile
|
|
||||||
import org.jetbrains.kotlin.android.synthetic.AndroidConst
|
|
||||||
import org.jetbrains.kotlin.android.synthetic.KotlinStringWriter
|
|
||||||
import org.jetbrains.kotlin.android.synthetic.escapeAndroidIdentifier
|
|
||||||
import org.jetbrains.kotlin.name.FqName
|
|
||||||
import org.jetbrains.kotlin.psi.KtFile
|
|
||||||
import org.jetbrains.kotlin.types.Flexibility
|
|
||||||
|
|
||||||
public class AndroidSyntheticFile(val name: String, val contents: String) {
|
|
||||||
companion object {
|
|
||||||
fun create(name: String, vararg contents: String) = AndroidSyntheticFile(name, contents.joinToString("\n\n"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract class SyntheticFileGenerator(protected val project: Project) {
|
|
||||||
|
|
||||||
public class NoAndroidManifestFound : Exception("No android manifest file found in project root")
|
|
||||||
|
|
||||||
public abstract val layoutXmlFileManager: AndroidLayoutXmlFileManager
|
|
||||||
|
|
||||||
public abstract fun getSyntheticFiles(): List<KtFile>
|
|
||||||
|
|
||||||
protected open fun generateSyntheticFiles(generateCommonFiles: Boolean, supportV4: Boolean): List<AndroidSyntheticFile> {
|
|
||||||
val commonFiles = if (generateCommonFiles) generateCommonFiles(supportV4) else listOf()
|
|
||||||
|
|
||||||
return layoutXmlFileManager.getLayoutXmlFiles().flatMap { variantData ->
|
|
||||||
variantData.flatMap { entry ->
|
|
||||||
val files = entry.value
|
|
||||||
val resources = extractLayoutResources(files)
|
|
||||||
|
|
||||||
val layoutName = entry.key
|
|
||||||
|
|
||||||
arrayListOf<AndroidSyntheticFile>().apply {
|
|
||||||
this += renderMainLayoutFiles(variantData.variant, layoutName, resources, supportV4)
|
|
||||||
this += renderViewLayoutFiles(variantData.variant, layoutName, resources)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}.filterNotNull() + commonFiles
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun generateCommonFiles(supportV4: Boolean): List<AndroidSyntheticFile> {
|
|
||||||
val renderSyntheticFile = renderSyntheticFile("clearCache") {
|
|
||||||
writePackage(AndroidConst.SYNTHETIC_PACKAGE)
|
|
||||||
writeAndroidImports()
|
|
||||||
writeClearCacheFunction(AndroidConst.ACTIVITY_FQNAME)
|
|
||||||
writeClearCacheFunction(AndroidConst.FRAGMENT_FQNAME)
|
|
||||||
if (supportV4) {
|
|
||||||
writeClearCacheFunction(AndroidConst.SUPPORT_FRAGMENT_FQNAME)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val clearCacheFile = renderSyntheticFile
|
|
||||||
|
|
||||||
return listOf(clearCacheFile, TOOLS_FILE)
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract fun extractLayoutResources(files: List<PsiFile>): List<AndroidResource>
|
|
||||||
|
|
||||||
protected abstract fun checkIfClassExist(fqName: String): Boolean
|
|
||||||
|
|
||||||
private fun renderMainLayoutFiles(
|
|
||||||
variant: AndroidVariant,
|
|
||||||
layoutName: String,
|
|
||||||
resources: List<AndroidResource>,
|
|
||||||
supportV4: Boolean
|
|
||||||
): List<AndroidSyntheticFile> {
|
|
||||||
return renderLayoutFile(variant, layoutName + AndroidConst.LAYOUT_POSTFIX, escapeAndroidIdentifier(layoutName), resources) {
|
|
||||||
val properties = it.mainProperties.toArrayList()
|
|
||||||
if (supportV4) properties.addAll(it.mainPropertiesForSupportV4)
|
|
||||||
properties
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun renderViewLayoutFiles(
|
|
||||||
variant: AndroidVariant,
|
|
||||||
layoutName: String,
|
|
||||||
resources: List<AndroidResource>
|
|
||||||
): List<AndroidSyntheticFile> {
|
|
||||||
return renderLayoutFile(variant, layoutName + AndroidConst.VIEW_LAYOUT_POSTFIX,
|
|
||||||
escapeAndroidIdentifier(layoutName) + ".view", resources) { it.viewProperties }
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun renderLayoutFile(
|
|
||||||
variant: AndroidVariant,
|
|
||||||
filename: String,
|
|
||||||
layoutName: String,
|
|
||||||
resources: List<AndroidResource>,
|
|
||||||
properties: (AndroidResource) -> List<Pair<String, String>>): List<AndroidSyntheticFile> {
|
|
||||||
fun render(defaultVariant: Boolean = false): AndroidSyntheticFile {
|
|
||||||
val fullFilename = (if (defaultVariant) "" else "${variant.name}_") + filename
|
|
||||||
return renderSyntheticFile(fullFilename) {
|
|
||||||
val packageName = if (defaultVariant)
|
|
||||||
AndroidConst.SYNTHETIC_PACKAGE + "." + layoutName
|
|
||||||
else
|
|
||||||
AndroidConst.SYNTHETIC_PACKAGE + '.' + variant.name + '.' + layoutName
|
|
||||||
|
|
||||||
writePackage(packageName)
|
|
||||||
writeAndroidImports()
|
|
||||||
|
|
||||||
for (res in resources) {
|
|
||||||
properties(res).forEach { property ->
|
|
||||||
if (defaultVariant) {
|
|
||||||
val deprecatedText = "Use the property from the 'main' variant instead"
|
|
||||||
val import = AndroidConst.SYNTHETIC_PACKAGE + '.' + variant.name + '.' + layoutName + '.' + res.id
|
|
||||||
writeText("@Deprecated(\"$deprecatedText\", ReplaceWith(\"${res.id}\", \"$import\"))")
|
|
||||||
}
|
|
||||||
writeSyntheticProperty(property.first, res, property.second)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return if (variant.isMainVariant) listOf(render(), render(true)) else listOf(render())
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun renderSyntheticFile(filename: String, init: KotlinStringWriter.() -> Unit): AndroidSyntheticFile {
|
|
||||||
val stringWriter = KotlinStringWriter()
|
|
||||||
stringWriter.init()
|
|
||||||
return AndroidSyntheticFile(filename, stringWriter.toStringBuffer().toString())
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun KotlinStringWriter.writeAndroidImports() {
|
|
||||||
ANDROID_IMPORTS.forEach { writeImport(it) }
|
|
||||||
writeEmptyLine()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun KotlinStringWriter.writeSyntheticProperty(receiver: String, resource: AndroidResource, stubCall: String) {
|
|
||||||
val className = if (isFromSupportV4Package(receiver)) resource.supportClassName else resource.className
|
|
||||||
val cast = if (resource.className != AndroidConst.VIEW_FQNAME) " as? $className" else ""
|
|
||||||
val body = arrayListOf("return $stubCall$cast")
|
|
||||||
|
|
||||||
// Annotation on wrong widget type
|
|
||||||
if (resource is AndroidWidget && resource.invalidType != null) {
|
|
||||||
writeText("@${INVALID_WIDGET_TYPE_ANNOTATION_FQNAME.asString()}(\"${resource.invalidType}\")")
|
|
||||||
}
|
|
||||||
|
|
||||||
writeImmutableExtensionProperty(receiver,
|
|
||||||
name = resource.id,
|
|
||||||
retType = "$EXPLICIT_FLEXIBLE_CLASS_NAME<$className, $className?>",
|
|
||||||
getterBody = body)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun KotlinStringWriter.writeClearCacheFunction(receiver: String) {
|
|
||||||
writeText("public fun $receiver.${AndroidConst.CLEAR_FUNCTION_NAME}() {}\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
protected fun <T> cachedValue(result: () -> Result<T>): CachedValue<T> {
|
|
||||||
return CachedValuesManager.getManager(project).createCachedValue(result, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun isFromSupportV4Package(fqName: String): Boolean {
|
|
||||||
return fqName.startsWith(AndroidConst.SUPPORT_V4_PACKAGE)
|
|
||||||
}
|
|
||||||
|
|
||||||
protected fun resolveFqClassNameForView(tag: String): String? {
|
|
||||||
if (tag.contains('.')) {
|
|
||||||
if (!checkIfClassExist(tag)) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
return tag
|
|
||||||
}
|
|
||||||
for (pkg in AndroidConst.FQNAME_RESOLVE_PACKAGES) {
|
|
||||||
val fqName = "$pkg.$tag"
|
|
||||||
if (checkIfClassExist(fqName)) {
|
|
||||||
return fqName
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
protected fun parseAndroidResource(id: String, tag: String, fqNameResolver: (String) -> String?): AndroidResource {
|
|
||||||
return when (tag) {
|
|
||||||
"fragment" -> AndroidFragment(id)
|
|
||||||
"include" -> AndroidWidget(id, AndroidConst.VIEW_FQNAME)
|
|
||||||
else -> parseAndroidWidget(id, tag, fqNameResolver)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract fun parseAndroidWidget(id: String, tag: String, fqNameResolver: (String) -> String?): AndroidResource
|
|
||||||
|
|
||||||
protected fun supportV4Available(): Boolean = checkIfClassExist(AndroidConst.SUPPORT_FRAGMENT_FQNAME)
|
|
||||||
|
|
||||||
protected fun filterDuplicates(resources: List<AndroidResource>): List<AndroidResource> {
|
|
||||||
val resourceMap = linkedMapOf<String, AndroidResource>()
|
|
||||||
val resourcesToExclude = hashSetOf<String>()
|
|
||||||
|
|
||||||
for (res in resources) {
|
|
||||||
if (resourceMap.contains(res.id)) {
|
|
||||||
val existing = resourceMap[res.id]!!
|
|
||||||
|
|
||||||
if (!res.sameClass(existing)) {
|
|
||||||
resourcesToExclude.add(res.id)
|
|
||||||
} else if (res is AndroidWidget && existing.className != res.className && existing.className != AndroidConst.VIEW_FQNAME) {
|
|
||||||
// Widgets with the same id but different types exist.
|
|
||||||
resourceMap.put(res.id, AndroidWidget(res.id, AndroidConst.VIEW_FQNAME))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else resourceMap.put(res.id, res)
|
|
||||||
}
|
|
||||||
resourcesToExclude.forEach { resourceMap.remove(it) }
|
|
||||||
return resourceMap.values().toList()
|
|
||||||
}
|
|
||||||
|
|
||||||
protected fun generateSyntheticJetFiles(files: List<AndroidSyntheticFile>): List<KtFile> {
|
|
||||||
val psiManager = PsiManager.getInstance(project)
|
|
||||||
val applicationPackage = layoutXmlFileManager.androidModule?.applicationPackage
|
|
||||||
|
|
||||||
return files.mapIndexed { index, syntheticFile ->
|
|
||||||
val fileName = AndroidConst.SYNTHETIC_FILENAME_PREFIX + syntheticFile.name + ".kt"
|
|
||||||
val virtualFile = LightVirtualFile(fileName, syntheticFile.contents)
|
|
||||||
val jetFile = psiManager.findFile(virtualFile) as KtFile
|
|
||||||
if (applicationPackage != null) {
|
|
||||||
jetFile.putUserData(AndroidConst.ANDROID_USER_PACKAGE, applicationPackage)
|
|
||||||
}
|
|
||||||
jetFile
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public companion object {
|
|
||||||
private val EXPLICIT_FLEXIBLE_PACKAGE = Flexibility.FLEXIBLE_TYPE_CLASSIFIER.packageFqName.asString()
|
|
||||||
private val EXPLICIT_FLEXIBLE_CLASS_NAME = Flexibility.FLEXIBLE_TYPE_CLASSIFIER.relativeClassName.asString()
|
|
||||||
|
|
||||||
private val ANDROID_IMPORTS = listOf(Flexibility.FLEXIBLE_TYPE_CLASSIFIER.asSingleFqName().asString())
|
|
||||||
|
|
||||||
private val INVALID_WIDGET_TYPE_ANNOTATION = "InvalidWidgetType"
|
|
||||||
public val INVALID_WIDGET_TYPE_ANNOTATION_TYPE_PARAMETER: String = "type"
|
|
||||||
public val INVALID_WIDGET_TYPE_ANNOTATION_FQNAME: FqName = FqName("$EXPLICIT_FLEXIBLE_PACKAGE.$INVALID_WIDGET_TYPE_ANNOTATION")
|
|
||||||
|
|
||||||
private val TOOLS_FILE = AndroidSyntheticFile.create("tools",
|
|
||||||
"package $EXPLICIT_FLEXIBLE_PACKAGE",
|
|
||||||
"class $EXPLICIT_FLEXIBLE_CLASS_NAME<L, U>",
|
|
||||||
"annotation class $INVALID_WIDGET_TYPE_ANNOTATION(public val $INVALID_WIDGET_TYPE_ANNOTATION_TYPE_PARAMETER: String)")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
+45
-44
@@ -16,7 +16,17 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.android.synthetic.res
|
package org.jetbrains.kotlin.android.synthetic.res
|
||||||
|
|
||||||
|
import com.intellij.openapi.project.Project
|
||||||
|
import com.intellij.psi.PsiElement
|
||||||
|
import com.intellij.psi.util.CachedValue
|
||||||
|
import com.intellij.psi.util.CachedValueProvider
|
||||||
|
import com.intellij.psi.util.CachedValuesManager
|
||||||
import org.jetbrains.kotlin.android.synthetic.AndroidConst
|
import org.jetbrains.kotlin.android.synthetic.AndroidConst
|
||||||
|
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||||
|
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||||
|
import org.jetbrains.kotlin.name.ClassId
|
||||||
|
import org.jetbrains.kotlin.name.FqName
|
||||||
|
import org.jetbrains.kotlin.serialization.deserialization.findClassAcrossModuleDependencies
|
||||||
|
|
||||||
class AndroidVariant(val name: String, val resDirectories: List<String>) {
|
class AndroidVariant(val name: String, val resDirectories: List<String>) {
|
||||||
val packageName: String = name
|
val packageName: String = name
|
||||||
@@ -28,61 +38,52 @@ class AndroidVariant(val name: String, val resDirectories: List<String>) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class AndroidModule(val applicationPackage: String, val variants: List<AndroidVariant>) {
|
class AndroidModule(val applicationPackage: String, val variants: List<AndroidVariant>) {
|
||||||
override fun equals(other: Any?) = other is AndroidModule && applicationPackage == other.applicationPackage
|
override fun equals(other: Any?) = other is AndroidModule && applicationPackage == other.applicationPackage
|
||||||
override fun hashCode() = applicationPackage.hashCode()
|
override fun hashCode() = applicationPackage.hashCode()
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract class AndroidResource(val id: String) {
|
sealed class AndroidResource(val id: String, val sourceElement: PsiElement?) {
|
||||||
public abstract val className: String
|
open fun sameClass(other: AndroidResource): Boolean = false
|
||||||
public open val supportClassName: String
|
|
||||||
get() = className
|
|
||||||
|
|
||||||
public abstract val mainProperties: List<Pair<String, String>>
|
class Widget(
|
||||||
public open val mainPropertiesForSupportV4: List<Pair<String, String>> = listOf()
|
id: String,
|
||||||
public open val viewProperties: List<Pair<String, String>> = listOf()
|
val xmlType: String,
|
||||||
|
sourceElement: PsiElement?
|
||||||
public open fun sameClass(other: AndroidResource): Boolean = false
|
) : AndroidResource(id, sourceElement) {
|
||||||
}
|
override fun sameClass(other: AndroidResource) = other is Widget
|
||||||
|
|
||||||
public class AndroidWidget(
|
|
||||||
id: String,
|
|
||||||
override val className: String,
|
|
||||||
val invalidType: String? = null // When widget type is invalid, this value is the widget tag name
|
|
||||||
) : AndroidResource(id) {
|
|
||||||
private companion object {
|
|
||||||
val MAIN_PROPERTIES = listOf(
|
|
||||||
AndroidConst.ACTIVITY_FQNAME to "findViewById(0)",
|
|
||||||
AndroidConst.FRAGMENT_FQNAME to "getView().findViewById(0)")
|
|
||||||
|
|
||||||
val MAIN_PROPERTIES_SUPPORT_V4 = listOf(AndroidConst.SUPPORT_FRAGMENT_FQNAME to "getView().findViewById(0)")
|
|
||||||
|
|
||||||
val VIEW_PROPERTIES = listOf(AndroidConst.VIEW_FQNAME to "findViewById(0)")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override val mainProperties = MAIN_PROPERTIES
|
class Fragment(id: String, sourceElement: PsiElement?) : AndroidResource(id, sourceElement) {
|
||||||
override val mainPropertiesForSupportV4 = MAIN_PROPERTIES_SUPPORT_V4
|
override fun sameClass(other: AndroidResource) = other is Fragment
|
||||||
override val viewProperties = VIEW_PROPERTIES
|
}
|
||||||
|
|
||||||
override fun sameClass(other: AndroidResource) = other is AndroidWidget
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class AndroidFragment(id: String) : AndroidResource(id) {
|
fun <T> cachedValue(project: Project, result: () -> CachedValueProvider.Result<T>): CachedValue<T> {
|
||||||
private companion object {
|
return CachedValuesManager.getManager(project).createCachedValue(result, false)
|
||||||
val MAIN_PROPERTIES = listOf(
|
}
|
||||||
AndroidConst.ACTIVITY_FQNAME to "getFragmentManager().findFragmentById(0)",
|
|
||||||
AndroidConst.FRAGMENT_FQNAME to "getFragmentManager().findFragmentById(0)")
|
|
||||||
|
|
||||||
val MAIN_PROPERTIES_SUPPORT_V4 = listOf(
|
class ResolvedWidget(val widget: AndroidResource.Widget, val viewClassDescriptor: ClassDescriptor?) {
|
||||||
AndroidConst.SUPPORT_FRAGMENT_FQNAME to "getFragmentManager().findFragmentById(0)",
|
val isErrorType: Boolean
|
||||||
AndroidConst.SUPPORT_FRAGMENT_ACTIVITY_FQNAME to "getSupportFragmentManager().findFragmentById(0)")
|
get() = viewClassDescriptor == null
|
||||||
|
|
||||||
|
val errorType: String?
|
||||||
|
get() = if (isErrorType) widget.xmlType else null
|
||||||
|
}
|
||||||
|
|
||||||
|
fun AndroidResource.Widget.resolve(module: ModuleDescriptor): ResolvedWidget {
|
||||||
|
fun resolve(fqName: String) = module.findClassAcrossModuleDependencies(ClassId.topLevel(FqName(fqName)))
|
||||||
|
|
||||||
|
if ('.' in xmlType) {
|
||||||
|
return ResolvedWidget(this, resolve(xmlType))
|
||||||
}
|
}
|
||||||
|
|
||||||
override val className = AndroidConst.FRAGMENT_FQNAME
|
for (packageName in AndroidConst.FQNAME_RESOLVE_PACKAGES) {
|
||||||
override val supportClassName = AndroidConst.SUPPORT_FRAGMENT_FQNAME
|
val classDescriptor = resolve("$packageName.$xmlType")
|
||||||
|
if (classDescriptor != null) {
|
||||||
|
return ResolvedWidget(this, classDescriptor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override val mainProperties = MAIN_PROPERTIES
|
return ResolvedWidget(this, null)
|
||||||
override val mainPropertiesForSupportV4 = MAIN_PROPERTIES_SUPPORT_V4
|
|
||||||
|
|
||||||
override fun sameClass(other: AndroidResource) = other is AndroidFragment
|
|
||||||
}
|
}
|
||||||
+137
@@ -0,0 +1,137 @@
|
|||||||
|
/*
|
||||||
|
* 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.android.synthetic.res
|
||||||
|
|
||||||
|
import com.intellij.psi.PsiElement
|
||||||
|
import org.jetbrains.kotlin.android.synthetic.AndroidConst
|
||||||
|
import org.jetbrains.kotlin.android.synthetic.descriptors.AndroidSyntheticPackageFragmentDescriptor
|
||||||
|
import org.jetbrains.kotlin.android.synthetic.descriptors.SyntheticElementResolveContext
|
||||||
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
|
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
||||||
|
import org.jetbrains.kotlin.descriptors.impl.PropertyDescriptorImpl
|
||||||
|
import org.jetbrains.kotlin.descriptors.impl.PropertyGetterDescriptorImpl
|
||||||
|
import org.jetbrains.kotlin.descriptors.impl.SimpleFunctionDescriptorImpl
|
||||||
|
import org.jetbrains.kotlin.name.Name
|
||||||
|
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
|
||||||
|
import org.jetbrains.kotlin.resolve.source.PsiSourceElement
|
||||||
|
import org.jetbrains.kotlin.types.*
|
||||||
|
import org.jetbrains.kotlin.types.typeUtil.makeNullable
|
||||||
|
|
||||||
|
private class XmlSourceElement(override val psi: PsiElement) : PsiSourceElement
|
||||||
|
|
||||||
|
internal fun genClearCacheFunction(packageFragmentDescriptor: PackageFragmentDescriptor, receiverType: KotlinType): FunctionDescriptor {
|
||||||
|
val function = object : AndroidSyntheticFunction, SimpleFunctionDescriptorImpl(
|
||||||
|
packageFragmentDescriptor,
|
||||||
|
null,
|
||||||
|
Annotations.EMPTY,
|
||||||
|
Name.identifier(AndroidConst.CLEAR_FUNCTION_NAME),
|
||||||
|
CallableMemberDescriptor.Kind.SYNTHESIZED,
|
||||||
|
SourceElement.NO_SOURCE) {}
|
||||||
|
|
||||||
|
val unitType = packageFragmentDescriptor.builtIns.unitType
|
||||||
|
function.initialize(receiverType, null, emptyList(), emptyList(), unitType, Modality.FINAL, Visibilities.PUBLIC)
|
||||||
|
return function
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun genPropertyForWidget(
|
||||||
|
packageFragmentDescriptor: AndroidSyntheticPackageFragmentDescriptor,
|
||||||
|
receiverType: KotlinType,
|
||||||
|
resolvedWidget: ResolvedWidget,
|
||||||
|
context: SyntheticElementResolveContext
|
||||||
|
): PropertyDescriptor {
|
||||||
|
val sourceEl = resolvedWidget.widget.sourceElement?.let { XmlSourceElement(it) } ?: SourceElement.NO_SOURCE
|
||||||
|
|
||||||
|
val classDescriptor = resolvedWidget.viewClassDescriptor
|
||||||
|
val type = classDescriptor?.let {
|
||||||
|
val defaultType = classDescriptor.defaultType
|
||||||
|
if (defaultType.constructor.parameters.isEmpty())
|
||||||
|
defaultType
|
||||||
|
else
|
||||||
|
KotlinTypeImpl.create(Annotations.EMPTY, classDescriptor, false,
|
||||||
|
defaultType.constructor.parameters.map { StarProjectionImpl(it) })
|
||||||
|
} ?: context.viewType
|
||||||
|
|
||||||
|
return genProperty(resolvedWidget.widget.id, receiverType, type, packageFragmentDescriptor, sourceEl, resolvedWidget.errorType)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun genPropertyForFragment(
|
||||||
|
packageFragmentDescriptor: PackageFragmentDescriptor,
|
||||||
|
receiverType: KotlinType,
|
||||||
|
type: KotlinType,
|
||||||
|
fragment: AndroidResource.Fragment
|
||||||
|
): PropertyDescriptor {
|
||||||
|
val sourceElement = fragment.sourceElement?.let { XmlSourceElement(it) } ?: SourceElement.NO_SOURCE
|
||||||
|
return genProperty(fragment.id, receiverType, type, packageFragmentDescriptor, sourceElement, null)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun genProperty(
|
||||||
|
id: String,
|
||||||
|
receiverType: KotlinType,
|
||||||
|
type: KotlinType,
|
||||||
|
containingDeclaration: DeclarationDescriptor,
|
||||||
|
sourceElement: SourceElement,
|
||||||
|
errorType: String?
|
||||||
|
): PropertyDescriptor {
|
||||||
|
val property = object : AndroidSyntheticProperty, PropertyDescriptorImpl(
|
||||||
|
containingDeclaration,
|
||||||
|
null,
|
||||||
|
Annotations.EMPTY,
|
||||||
|
Modality.FINAL,
|
||||||
|
Visibilities.PUBLIC,
|
||||||
|
false,
|
||||||
|
Name.identifier(id),
|
||||||
|
CallableMemberDescriptor.Kind.SYNTHESIZED,
|
||||||
|
sourceElement,
|
||||||
|
false,
|
||||||
|
false) {
|
||||||
|
override val errorType = errorType
|
||||||
|
}
|
||||||
|
|
||||||
|
val flexibleType = DelegatingFlexibleType.create(type, type.makeNullable(), FlexibleTypeCapabilities.NONE)
|
||||||
|
property.setType(
|
||||||
|
flexibleType,
|
||||||
|
emptyList<TypeParameterDescriptor>(),
|
||||||
|
null,
|
||||||
|
receiverType)
|
||||||
|
|
||||||
|
val getter = PropertyGetterDescriptorImpl(
|
||||||
|
property,
|
||||||
|
Annotations.EMPTY,
|
||||||
|
Modality.FINAL,
|
||||||
|
Visibilities.PUBLIC,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
CallableMemberDescriptor.Kind.SYNTHESIZED,
|
||||||
|
null,
|
||||||
|
SourceElement.NO_SOURCE)
|
||||||
|
|
||||||
|
getter.initialize(null)
|
||||||
|
|
||||||
|
property.initialize(getter, null)
|
||||||
|
|
||||||
|
return property
|
||||||
|
}
|
||||||
|
|
||||||
|
interface AndroidSyntheticFunction
|
||||||
|
|
||||||
|
interface AndroidSyntheticProperty {
|
||||||
|
val errorType: String?
|
||||||
|
|
||||||
|
val isErrorType: Boolean
|
||||||
|
get() = errorType != null
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user