Add intentions Add/Remove/Redo parcelable implementation
#KT-17465 Fixed #KT-12049 Fixed
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.android
|
||||
|
||||
import org.jetbrains.kotlin.idea.editor.fixers.range
|
||||
import org.jetbrains.kotlin.psi.KtClass
|
||||
|
||||
internal fun KtClass.insideBody(offset: Int): Boolean = getBody()?.range?.contains(offset) ?: false
|
||||
@@ -0,0 +1,470 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.android
|
||||
|
||||
import com.android.SdkConstants.CLASS_PARCEL
|
||||
import com.android.SdkConstants.CLASS_PARCELABLE
|
||||
import com.intellij.openapi.fileEditor.FileDocumentManager
|
||||
import com.intellij.psi.PsiDocumentManager
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.builtins.PrimitiveType
|
||||
import org.jetbrains.kotlin.descriptors.ParameterDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||
import org.jetbrains.kotlin.idea.codeInsight.shorten.addToShorteningWaitSet
|
||||
import org.jetbrains.kotlin.idea.codeInsight.shorten.performDelayedRefactoringRequests
|
||||
import org.jetbrains.kotlin.idea.intentions.getLeftMostReceiverExpression
|
||||
import org.jetbrains.kotlin.idea.search.usagesSearch.descriptor
|
||||
import org.jetbrains.kotlin.idea.search.usagesSearch.propertyDescriptor
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
|
||||
|
||||
private val CREATOR_NAME = "CREATOR"
|
||||
private val PARCEL_NAME = "parcel"
|
||||
private val CREATOR_TEXT =
|
||||
"companion object $CREATOR_NAME : android.os.Parcelable.Creator<%1\$s> {\n" +
|
||||
" override fun createFromParcel($PARCEL_NAME: $CLASS_PARCEL): %1\$s {\n" +
|
||||
" return %1\$s($PARCEL_NAME)\n" +
|
||||
" }\n\n" +
|
||||
" override fun newArray(size: Int): Array<%1\$s?> {\n" +
|
||||
" return arrayOfNulls(size)\n" +
|
||||
" }\n" +
|
||||
"}"
|
||||
private val WRITE_TO_PARCEL_TEXT = "override fun writeToParcel($PARCEL_NAME: $CLASS_PARCEL, flags: Int) {\n}"
|
||||
private val WRITE_TO_PARCEL_SUPER_CALL_TEXT = "super.writeToParcel($PARCEL_NAME, flags)"
|
||||
private val WRITE_TO_PARCEL_WITH_SUPER_TEXT =
|
||||
"override fun writeToParcel($PARCEL_NAME: $CLASS_PARCEL, flags: Int) {\n$WRITE_TO_PARCEL_SUPER_CALL_TEXT\n}"
|
||||
private val DESCRIBE_CONTENTS_TEXT = "override fun describeContents(): Int {\nreturn 0\n}"
|
||||
private val CONSTRUCTOR_TEXT = "constructor($PARCEL_NAME: $CLASS_PARCEL)"
|
||||
|
||||
|
||||
fun KtClass.canAddParcelable(): Boolean =
|
||||
findParcelableSupertype() == null
|
||||
|| findCreator() == null
|
||||
|| findConstructorFromParcel() == null
|
||||
|| findWriteToParcel() == null
|
||||
|| findDescribeContents() == null
|
||||
|
||||
fun KtClass.canRedoParcelable(): Boolean = canRemoveParcelable()
|
||||
|
||||
fun KtClass.canRemoveParcelable(): Boolean =
|
||||
findParcelableSupertype()?.takeIf { it.typeReference?.isParcelableReference() ?: false }
|
||||
?: findCreator()
|
||||
?: findConstructorFromParcel()
|
||||
?: findWriteToParcel()
|
||||
?: findDescribeContents() != null
|
||||
|
||||
fun KtClass.implementParcelable() {
|
||||
val factory = KtPsiFactory(this)
|
||||
val superExtendsParcelable = superExtendsParcelable()
|
||||
|
||||
findOrCreateParcelableSupertype(factory)
|
||||
val constructor = findOrCreateConstructor(factory)
|
||||
|
||||
addFieldReads(constructor, factory)
|
||||
|
||||
val writeToParcel = findOrCreateWriteToParcel(factory, superExtendsParcelable)
|
||||
addFieldWrites(writeToParcel, factory, superExtendsParcelable)
|
||||
|
||||
findOrCreateDescribeContents(factory)
|
||||
findOrCreateCreator(factory)
|
||||
|
||||
performDelayedRefactoringRequests(project)
|
||||
save()
|
||||
}
|
||||
|
||||
fun KtClass.removeParcelableImplementation() {
|
||||
findParcelableSupertype()?.takeIf { it.typeReference?.isParcelableReference() ?: false }?.let {
|
||||
removeSuperTypeListEntry(it)
|
||||
}
|
||||
|
||||
findConstructorFromParcel()?.let {
|
||||
if (it is KtPrimaryConstructor) {
|
||||
tryDeleteInitBlock()
|
||||
}
|
||||
it.delete()
|
||||
}
|
||||
|
||||
findWriteToParcel()?.delete()
|
||||
findDescribeContents()?.delete()
|
||||
findCreator()?.delete()
|
||||
save()
|
||||
}
|
||||
|
||||
fun KtClass.reimplementParcelable() {
|
||||
removeParcelableImplementation()
|
||||
implementParcelable()
|
||||
}
|
||||
|
||||
private fun KtClass.findCreator(): KtObjectDeclaration? = companionObjects.find { it.name == CREATOR_NAME }
|
||||
|
||||
private fun KtClass.findParcelableSupertype(): KtSuperTypeListEntry? = getSuperTypeList()?.findParcelable()
|
||||
|
||||
private fun KtSuperTypeList.findParcelable() = entries?.find { it.typeReference?.isParcelableSuccessorReference() ?: false }
|
||||
|
||||
private fun KtTypeReference.isParcelableSuccessorReference() =
|
||||
analyze(BodyResolveMode.PARTIAL)[BindingContext.TYPE, this]?.isSubclassOfParcelable() ?: false
|
||||
|
||||
private fun KtClass.superExtendsParcelable() = superTypeListEntries.find { it.typeReference?.extendsParcelable() ?: false } != null
|
||||
|
||||
private fun KtClass.tryDeleteInitBlock() {
|
||||
getAnonymousInitializers().forEach { initializer ->
|
||||
val block = initializer.body as? KtBlockExpression
|
||||
if (block != null) {
|
||||
block.statements.forEach { statement ->
|
||||
if (statement.isReadFromParcelPropertyAssignment()) {
|
||||
statement.delete()
|
||||
}
|
||||
}
|
||||
|
||||
if (block.statements.isEmpty()) {
|
||||
initializer.delete()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun KtExpression.isReadFromParcelPropertyAssignment(): Boolean {
|
||||
if (this !is KtBinaryExpression || this.operationToken != KtTokens.EQ) {
|
||||
return false
|
||||
}
|
||||
|
||||
return this.right?.isReadFromParcel() ?: false
|
||||
}
|
||||
|
||||
private fun KtExpression.isReadFromParcel(): Boolean {
|
||||
val reference = firstChild as? KtReferenceExpression
|
||||
?: (firstChild as? KtDotQualifiedExpression)?.getLeftMostReceiverExpression() as? KtReferenceExpression ?: return false
|
||||
val target = reference.analyze(BodyResolveMode.PARTIAL)[BindingContext.REFERENCE_TARGET, reference] ?: return false
|
||||
return (target as? ParameterDescriptor)?.type?.fqNameEquals(CLASS_PARCEL) ?: false
|
||||
}
|
||||
|
||||
private fun KtClass.addFieldWrites(function: KtFunction, factory: KtPsiFactory, callSuper: Boolean) {
|
||||
val bodyExpression = function.bodyExpression
|
||||
if (bodyExpression !is KtBlockExpression || !bodyExpression.isEmptyWriteToParcel(callSuper)) {
|
||||
return
|
||||
}
|
||||
|
||||
val propertyParameterDescriptors = primaryConstructor?.valueParameters?.mapNotNull { it.propertyDescriptor } ?: emptyList()
|
||||
|
||||
val propertyDescriptors = declarations
|
||||
.filter { it.isParcelableProperty() }
|
||||
.mapNotNull { it.descriptor as? PropertyDescriptor }
|
||||
|
||||
val parcelName = function.valueParameters[0].name ?: return
|
||||
val flagsName = function.valueParameters[1].name ?: return
|
||||
val blockText =
|
||||
(propertyParameterDescriptors + propertyDescriptors)
|
||||
.mapNotNull { it.formatWriteToParcel(parcelName, flagsName) }
|
||||
.joinToString(separator = "\n")
|
||||
|
||||
val block = factory.createBlock(
|
||||
if (callSuper)
|
||||
WRITE_TO_PARCEL_SUPER_CALL_TEXT + if (blockText.isNotBlank()) "\n$blockText" else ""
|
||||
else blockText
|
||||
)
|
||||
|
||||
bodyExpression.replace(block)
|
||||
}
|
||||
|
||||
private fun KtClass.addFieldReads(constructor: KtConstructor<*>, factory: KtPsiFactory) {
|
||||
val bodyExpression = constructor.getBodyExpression()
|
||||
if (bodyExpression != null && bodyExpression.statements.isNotEmpty()) {
|
||||
return
|
||||
}
|
||||
|
||||
val parcelName = constructor.getValueParameters().firstOrNull()?.name ?: return
|
||||
val parcelableProperties = declarations
|
||||
.filter { it.isParcelableProperty() }
|
||||
.mapNotNull { it.descriptor as? PropertyDescriptor }
|
||||
|
||||
if (parcelableProperties.isEmpty()) {
|
||||
return
|
||||
}
|
||||
|
||||
val blockText = parcelableProperties
|
||||
.mapNotNull { descriptor -> descriptor.formatReadFromParcel(parcelName)?.let { "${descriptor.name} = $it" } }
|
||||
.joinToString(separator = "\n")
|
||||
|
||||
val block = factory.createBlock(blockText)
|
||||
|
||||
if (constructor is KtPrimaryConstructor) {
|
||||
val initializer = factory.createAnonymousInitializer()
|
||||
initializer.body?.replace(block)
|
||||
addDeclaration(initializer).apply {
|
||||
addNewLineBeforeDeclaration()
|
||||
addToShorteningWaitSet()
|
||||
}
|
||||
}
|
||||
else {
|
||||
bodyExpression?.replace(block) ?: constructor.add(block)
|
||||
}
|
||||
}
|
||||
|
||||
private fun KtDeclaration.isParcelableProperty(): Boolean =
|
||||
this is KtProperty && isVar && !hasDelegate() && !isTransient() && getter == null && setter == null
|
||||
|
||||
private fun KtProperty.isTransient() = annotationEntries.find { it.isTransientAnnotation() } != null
|
||||
|
||||
private fun KtAnnotationEntry.isTransientAnnotation(): Boolean =
|
||||
typeReference?.analyze(BodyResolveMode.PARTIAL)?.get(BindingContext.TYPE, typeReference)?.fqNameEquals("kotlin.jvm.Transient") ?: false
|
||||
|
||||
private fun KtExpression.isCallToSuperWriteToParcel() =
|
||||
this is KtDotQualifiedExpression
|
||||
&& receiverExpression is KtSuperExpression
|
||||
&& (selectorExpression as? KtCallExpression)?.calleeExpression?.text == "writeToParcel"
|
||||
|
||||
private fun KtBlockExpression.isEmptyWriteToParcel(callSuper: Boolean): Boolean =
|
||||
if (callSuper) {
|
||||
statements.isEmpty() || statements.size == 1 && statements.first().isCallToSuperWriteToParcel()
|
||||
}
|
||||
else {
|
||||
statements.isEmpty()
|
||||
}
|
||||
|
||||
private fun PropertyDescriptor.formatReadFromParcel(parcelName: String): String? {
|
||||
val type = returnType ?: return null
|
||||
|
||||
fun KotlinType.formatJavaClassloader() = "${this.constructor.declarationDescriptor?.fqNameSafe}::class.java.classLoader"
|
||||
|
||||
// There is no read/write short array methods
|
||||
if (KotlinBuiltIns.isPrimitiveArray(type) && KotlinBuiltIns.getPrimitiveArrayElementType(type) != PrimitiveType.SHORT) {
|
||||
return "$parcelName.create${type.getName()}()"
|
||||
}
|
||||
|
||||
|
||||
if (type.isMarkedNullable && KotlinBuiltIns.isPrimitiveTypeOrNullablePrimitiveType(type)) {
|
||||
return "$parcelName.readValue(${type.formatJavaClassloader()}) as? ${type.getName()}"
|
||||
}
|
||||
|
||||
if (KotlinBuiltIns.isPrimitiveType(type)) {
|
||||
return when {
|
||||
KotlinBuiltIns.isBoolean(type) -> "$parcelName.readByte() != 0.toByte()"
|
||||
KotlinBuiltIns.isShort(type) -> "$parcelName.readInt().toShort()"
|
||||
KotlinBuiltIns.isChar(type) -> "$parcelName.readInt().toChar()"
|
||||
else -> "$parcelName.read${type.getName()}()"
|
||||
}
|
||||
}
|
||||
|
||||
return when {
|
||||
KotlinBuiltIns.isCharSequenceOrNullableCharSequence(type) -> "$parcelName.readString()"
|
||||
KotlinBuiltIns.isStringOrNullableString(type) -> "$parcelName.readString()"
|
||||
type.isArrayOfParcelable() -> "$parcelName.createTypedArray(${type.arguments.single().type.getName()}.CREATOR)"
|
||||
type.isListOfParcelable() -> "$parcelName.createTypedArrayList(${type.arguments.single().type.getName()}.CREATOR)"
|
||||
type.isArrayOfIBinder() -> "$parcelName.createBinderArray()"
|
||||
type.isListOfIBinder() -> "$parcelName.createBinderArrayList()"
|
||||
type.isArrayOfString() -> "$parcelName.createStringArray()"
|
||||
type.isListOfString() -> "$parcelName.createStringArrayList()"
|
||||
type.isSparseBooleanArray() -> "$parcelName.readSparseBooleanArray()"
|
||||
type.isBundle() -> "$parcelName.readBundle(${type.formatJavaClassloader()})"
|
||||
type.isIBinder() -> "$parcelName.readStrongBinder()"
|
||||
type.isSubclassOfParcelable(true) -> "$parcelName.readParcelable(${type.formatJavaClassloader()})" // This one should go last
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
private fun PropertyDescriptor.formatWriteToParcel(parcelName: String, flagsName: String): String? {
|
||||
val type = returnType ?: return null
|
||||
|
||||
// There is no read/write short array methods
|
||||
if (KotlinBuiltIns.isPrimitiveArray(type) && KotlinBuiltIns.getPrimitiveArrayElementType(type) != PrimitiveType.SHORT) {
|
||||
return "$parcelName.write${type.getName()}($name)"
|
||||
}
|
||||
|
||||
if (type.isMarkedNullable) {
|
||||
if (KotlinBuiltIns.isPrimitiveTypeOrNullablePrimitiveType(type)) {
|
||||
return "$parcelName.writeValue($name)"
|
||||
}
|
||||
else if (KotlinBuiltIns.isCharSequenceOrNullableCharSequence(type)) {
|
||||
return "$parcelName.writeString($name?.toString())"
|
||||
}
|
||||
}
|
||||
|
||||
if (KotlinBuiltIns.isPrimitiveType(type)) {
|
||||
return when {
|
||||
KotlinBuiltIns.isBoolean(type) -> "$parcelName.writeByte(if ($name) 1 else 0)"
|
||||
KotlinBuiltIns.isShort(type) -> "$parcelName.writeInt($name.toInt())"
|
||||
KotlinBuiltIns.isChar(type) -> "$parcelName.writeInt($name.toInt())"
|
||||
else -> "$parcelName.write${type.getName()}($name)"
|
||||
}
|
||||
}
|
||||
|
||||
return when {
|
||||
KotlinBuiltIns.isCharSequence(type) -> "$parcelName.writeString($name.toString())"
|
||||
KotlinBuiltIns.isStringOrNullableString(type) -> "$parcelName.writeString($name)"
|
||||
type.isArrayOfParcelable() -> "$parcelName.writeTypedArray($name, $flagsName)"
|
||||
type.isListOfParcelable() -> "$parcelName.writeTypedList($name)"
|
||||
type.isArrayOfIBinder() -> "$parcelName.writeBinderArray($name)"
|
||||
type.isListOfIBinder() -> "$parcelName.writeBinderList($name)"
|
||||
type.isArrayOfString() -> "$parcelName.writeStringArray($name)"
|
||||
type.isListOfString() -> "$parcelName.writeStringList($name)"
|
||||
type.isSparseBooleanArray() -> "$parcelName.writeSparseBooleanArray($name)"
|
||||
type.isBundle() -> "$parcelName.writeBundle($name)"
|
||||
type.isIBinder() -> "$parcelName.writeStrongBinder($name)"
|
||||
type.isSubclassOfParcelable(true) -> "$parcelName.writeParcelable($name, $flagsName)" // This one should go last
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
private fun KtClass.findOrCreateCreator(factory: KtPsiFactory): KtClassOrObject {
|
||||
findCreator()?.let {
|
||||
return it
|
||||
}
|
||||
|
||||
val creator = factory.createObject(CREATOR_TEXT.format(name))
|
||||
return addDeclaration(creator).apply { addToShorteningWaitSet() }
|
||||
}
|
||||
|
||||
private fun KtClass.findOrCreateParcelableSupertype(factory: KtPsiFactory): KtSuperTypeListEntry? {
|
||||
findParcelableSupertype()?.let {
|
||||
return it
|
||||
}
|
||||
|
||||
val supertypeEntry = factory.createSuperTypeEntry(CLASS_PARCELABLE)
|
||||
return addSuperTypeListEntry(supertypeEntry).apply { addToShorteningWaitSet() }
|
||||
}
|
||||
|
||||
private fun KtClass.save() = FileDocumentManager.getInstance().getDocument(containingFile.virtualFile)?.let {
|
||||
PsiDocumentManager.getInstance(project).commitDocument(it)
|
||||
}
|
||||
|
||||
private fun KtClass.findOrCreateConstructor(factory: KtPsiFactory): KtConstructor<*> {
|
||||
findConstructorFromParcel()?.let {
|
||||
return it
|
||||
}
|
||||
|
||||
createPrimaryConstructorIfAbsent()
|
||||
return createSecondaryConstructor(factory).apply {
|
||||
addToShorteningWaitSet()
|
||||
}
|
||||
}
|
||||
|
||||
private fun KtClass.createSecondaryConstructor(factory: KtPsiFactory): KtConstructor<*> {
|
||||
val constructorText = primaryConstructor?.let { constructor ->
|
||||
val arguments = constructor.valueParameters.map {
|
||||
it?.propertyDescriptor?.formatReadFromParcel(PARCEL_NAME) ?: "TODO(\"${it?.name}\")"
|
||||
}
|
||||
|
||||
val argumentList = arguments.joinToString(
|
||||
prefix = if (arguments.size > 1) "(\n" else "(",
|
||||
postfix = ")",
|
||||
separator = if (arguments.size > 1) ",\n" else ", ")
|
||||
|
||||
"$CONSTRUCTOR_TEXT :this$argumentList {\n}"
|
||||
} ?: "$CONSTRUCTOR_TEXT {\n}"
|
||||
|
||||
val constructor = factory.createSecondaryConstructor(constructorText)
|
||||
val lastProperty = declarations.findLast { it is KtProperty }
|
||||
if (lastProperty != null) {
|
||||
return addDeclarationAfter(constructor, lastProperty).apply { addNewLineBeforeDeclaration() }
|
||||
}
|
||||
else {
|
||||
val firstFunction = declarations.find { it is KtFunction }
|
||||
return addDeclarationBefore(constructor, firstFunction).apply { addNewLineBeforeDeclaration() }
|
||||
}
|
||||
}
|
||||
|
||||
private fun KtTypeReference.extendsParcelable(): Boolean =
|
||||
analyze(BodyResolveMode.PARTIAL)[BindingContext.TYPE, this]?.isSubclassOfParcelable(true) ?: false
|
||||
|
||||
private fun KtClass.findWriteToParcel() = declarations.find { it.isWriteToParcel() }
|
||||
|
||||
private fun KtDeclaration.isWriteToParcel(): Boolean = this is KtFunction && name == "writeToParcel" && valueParameters.let {
|
||||
it.size == 2 && it[0].isParcelParameter() && (it[1].typeReference?.fqNameEquals("kotlin.Int") ?: false)
|
||||
}
|
||||
|
||||
private fun KtClass.findOrCreateWriteToParcel(factory: KtPsiFactory, callSuper: Boolean): KtFunction {
|
||||
findWriteToParcel()?.let {
|
||||
return it as KtFunction
|
||||
}
|
||||
|
||||
val writeToParcel = factory.createFunction(if (callSuper) WRITE_TO_PARCEL_WITH_SUPER_TEXT else WRITE_TO_PARCEL_TEXT)
|
||||
return addDeclaration(writeToParcel).apply {
|
||||
addNewLineBeforeDeclaration()
|
||||
addToShorteningWaitSet()
|
||||
}
|
||||
}
|
||||
|
||||
private fun KtClass.findDescribeContents() = declarations.find { it.isDescribeContents() }
|
||||
|
||||
private fun KtDeclaration.isDescribeContents(): Boolean = this is KtFunction &&
|
||||
name == "describeContents" &&
|
||||
valueParameters.isEmpty()
|
||||
|
||||
private fun KtClass.findOrCreateDescribeContents(factory: KtPsiFactory): KtFunction {
|
||||
findDescribeContents()?.let {
|
||||
return it as KtFunction
|
||||
}
|
||||
|
||||
val describeContents = factory.createFunction(DESCRIBE_CONTENTS_TEXT)
|
||||
return addDeclaration(describeContents).apply {
|
||||
addNewLineBeforeDeclaration()
|
||||
addToShorteningWaitSet()
|
||||
}
|
||||
}
|
||||
|
||||
private fun KtClass.findConstructorFromParcel(): KtConstructor<*>? =
|
||||
primaryConstructor?.takeIf { it.isConstructorFromParcel() } ?: secondaryConstructors.find { it.isConstructorFromParcel() }
|
||||
|
||||
private fun KtConstructor<*>.isConstructorFromParcel(): Boolean = getValueParameters().let {
|
||||
it.size == 1 && it.single().isParcelParameter()
|
||||
}
|
||||
|
||||
private fun KtParameter.isParcelParameter(): Boolean = typeReference?.fqNameEquals(CLASS_PARCEL) ?: false
|
||||
|
||||
private fun KtTypeReference.isParcelableReference() = fqNameEquals(CLASS_PARCELABLE)
|
||||
|
||||
private fun KtTypeReference.fqNameEquals(fqName: String) =
|
||||
analyze(BodyResolveMode.PARTIAL)[BindingContext.TYPE, this]?.fqNameEquals(fqName) ?: false
|
||||
|
||||
private fun KotlinType.getName() = constructor.declarationDescriptor?.name
|
||||
|
||||
private fun KotlinType.fqNameEquals(fqName: String) = constructor.declarationDescriptor?.fqNameSafe?.asString() == fqName
|
||||
|
||||
private fun KotlinType.isSubclassOfParcelable(strict: Boolean = false): Boolean = isSubclassOf(FqName(CLASS_PARCELABLE), strict)
|
||||
|
||||
private fun KotlinType.isIBinder(): Boolean = fqNameEquals("android.os.IBinder")
|
||||
|
||||
private fun KotlinType.isArrayOfParcelable(): Boolean =
|
||||
KotlinBuiltIns.isArray(this) && arguments.singleOrNull()?.type?.isSubclassOfParcelable(true) ?: false
|
||||
|
||||
private fun KotlinType.isArrayOfIBinder(): Boolean =
|
||||
KotlinBuiltIns.isArray(this) && arguments.singleOrNull()?.type?.isIBinder() ?: false
|
||||
|
||||
private fun KotlinType.isArrayOfString(): Boolean =
|
||||
KotlinBuiltIns.isArray(this) && KotlinBuiltIns.isStringOrNullableString(arguments.singleOrNull()?.type)
|
||||
|
||||
private fun KotlinType.isListOfString(): Boolean =
|
||||
KotlinBuiltIns.isListOrNullableList(this) && KotlinBuiltIns.isStringOrNullableString(arguments.singleOrNull()?.type)
|
||||
|
||||
private fun KotlinType.isListOfParcelable(): Boolean =
|
||||
KotlinBuiltIns.isListOrNullableList(this) && arguments.singleOrNull()?.type?.isSubclassOfParcelable(true) ?: false
|
||||
|
||||
private fun KotlinType.isListOfIBinder(): Boolean =
|
||||
KotlinBuiltIns.isListOrNullableList(this) && arguments.singleOrNull()?.type?.isIBinder() ?: false
|
||||
|
||||
private fun KotlinType.isSparseBooleanArray(): Boolean = fqNameEquals("android.util.SparseBooleanArray")
|
||||
|
||||
private fun KotlinType.isBundle(): Boolean = fqNameEquals("android.os.Bundle")
|
||||
|
||||
private fun <T: KtDeclaration> T.addNewLineBeforeDeclaration() = parent.addBefore(KtPsiFactory(this).createNewLine(), this)
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.android
|
||||
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
internal fun KotlinType.fqNameSafe() = constructor.declarationDescriptor?.fqNameSafe
|
||||
|
||||
internal fun KotlinType.isSubclassOf(className: FqName, strict: Boolean = false): Boolean =
|
||||
(!strict && fqNameSafe() == className) || constructor.supertypes.any {
|
||||
className == it.fqNameSafe() || it.isSubclassOf(className, true)
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.android.intention
|
||||
|
||||
import com.intellij.codeInsight.intention.HighPriorityAction
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import org.jetbrains.android.facet.AndroidFacet
|
||||
import org.jetbrains.android.util.AndroidBundle
|
||||
import org.jetbrains.kotlin.android.canAddParcelable
|
||||
import org.jetbrains.kotlin.android.implementParcelable
|
||||
import org.jetbrains.kotlin.android.insideBody
|
||||
import org.jetbrains.kotlin.idea.intentions.SelfTargetingIntention
|
||||
import org.jetbrains.kotlin.psi.KtClass
|
||||
|
||||
|
||||
class ImplementParcelableAction :
|
||||
SelfTargetingIntention<KtClass>(KtClass::class.java, AndroidBundle.message("implement.parcelable.intention.text")),
|
||||
HighPriorityAction {
|
||||
override fun isApplicableTo(element: KtClass, caretOffset: Int): Boolean =
|
||||
AndroidFacet.getInstance(element) != null &&
|
||||
!element.insideBody(caretOffset) &&
|
||||
element.canAddParcelable()
|
||||
|
||||
override fun applyTo(element: KtClass, editor: Editor?) {
|
||||
element.implementParcelable()
|
||||
}
|
||||
|
||||
override fun startInWriteAction(): Boolean = true
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.android.intention
|
||||
|
||||
import com.intellij.codeInsight.intention.HighPriorityAction
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import org.jetbrains.android.facet.AndroidFacet
|
||||
import org.jetbrains.android.util.AndroidBundle
|
||||
import org.jetbrains.kotlin.android.canRedoParcelable
|
||||
import org.jetbrains.kotlin.android.insideBody
|
||||
import org.jetbrains.kotlin.android.reimplementParcelable
|
||||
import org.jetbrains.kotlin.idea.intentions.SelfTargetingIntention
|
||||
import org.jetbrains.kotlin.psi.KtClass
|
||||
|
||||
|
||||
class RedoParcelableAction :
|
||||
SelfTargetingIntention<KtClass>(KtClass::class.java, AndroidBundle.message("redo.parcelable.intention.text")),
|
||||
HighPriorityAction {
|
||||
override fun isApplicableTo(element: KtClass, caretOffset: Int): Boolean =
|
||||
AndroidFacet.getInstance(element) != null &&
|
||||
!element.insideBody(caretOffset) &&
|
||||
element.canRedoParcelable()
|
||||
|
||||
override fun applyTo(element: KtClass, editor: Editor?) {
|
||||
element.reimplementParcelable()
|
||||
}
|
||||
|
||||
override fun startInWriteAction(): Boolean = true
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.android.intention
|
||||
|
||||
import com.intellij.codeInsight.intention.HighPriorityAction
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import org.jetbrains.android.facet.AndroidFacet
|
||||
import org.jetbrains.android.util.AndroidBundle
|
||||
import org.jetbrains.kotlin.android.canRemoveParcelable
|
||||
import org.jetbrains.kotlin.android.insideBody
|
||||
import org.jetbrains.kotlin.android.removeParcelableImplementation
|
||||
import org.jetbrains.kotlin.idea.intentions.SelfTargetingIntention
|
||||
import org.jetbrains.kotlin.psi.KtClass
|
||||
|
||||
|
||||
class RemoveParcelableAction :
|
||||
SelfTargetingIntention<KtClass>(KtClass::class.java, AndroidBundle.message("remove.parcelable.intention.text")),
|
||||
HighPriorityAction {
|
||||
override fun isApplicableTo(element: KtClass, caretOffset: Int): Boolean =
|
||||
AndroidFacet.getInstance(element) != null &&
|
||||
!element.insideBody(caretOffset) &&
|
||||
element.canRemoveParcelable()
|
||||
|
||||
override fun applyTo(element: KtClass, editor: Editor?) {
|
||||
element.removeParcelableImplementation()
|
||||
}
|
||||
|
||||
override fun startInWriteAction(): Boolean = true
|
||||
}
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.android.intention
|
||||
|
||||
import com.android.SdkConstants
|
||||
import com.intellij.codeInsight.intention.IntentionAction
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import com.intellij.util.PathUtil
|
||||
import org.jetbrains.kotlin.android.KotlinAndroidTestCase
|
||||
import org.jetbrains.kotlin.idea.test.ConfigLibraryUtil
|
||||
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase
|
||||
import org.jetbrains.kotlin.test.InTextDirectivesUtils
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils
|
||||
import java.io.File
|
||||
|
||||
|
||||
abstract class AbstractAndroidIntentionTest : KotlinAndroidTestCase() {
|
||||
fun doTest(path: String) {
|
||||
val testFile = File(path)
|
||||
val testFileText = FileUtil.loadFile(testFile)
|
||||
val intentionClassName = InTextDirectivesUtils.findStringWithPrefixes(testFileText, "// INTENTION_CLASS: ")
|
||||
?: error("Intention class not found!")
|
||||
|
||||
val notAvailable = InTextDirectivesUtils.isDirectiveDefined(testFileText, "// NOT_AVAILABLE")
|
||||
val withRuntime = InTextDirectivesUtils.isDirectiveDefined(testFileText, "// WITH_RUNTIME")
|
||||
|
||||
try {
|
||||
if (withRuntime) {
|
||||
ConfigLibraryUtil.configureKotlinRuntime(myFixture.module)
|
||||
}
|
||||
|
||||
val sourceFile = myFixture.copyFileToProject(path, "src/${PathUtil.getFileName(path)}")
|
||||
myFixture.configureFromExistingVirtualFile(sourceFile)
|
||||
|
||||
val intention = Class.forName(intentionClassName).newInstance() as? IntentionAction ?: error("Failed to create intention!")
|
||||
if (!intention.isAvailable(myFixture.project, myFixture.editor, myFixture.file)) {
|
||||
if (notAvailable) {
|
||||
return
|
||||
}
|
||||
|
||||
error("Intention is not available!")
|
||||
}
|
||||
|
||||
myFixture.launchAction(intention)
|
||||
myFixture.checkResultByFile("$path.expected")
|
||||
}
|
||||
finally {
|
||||
if (withRuntime) {
|
||||
ConfigLibraryUtil.unConfigureKotlinRuntime(myFixture.module)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun createManifest() {
|
||||
myFixture.copyFileToProject("idea/testData/android/AndroidManifest.xml", SdkConstants.FN_ANDROID_MANIFEST_XML)
|
||||
}
|
||||
|
||||
override fun getTestDataPath() = KotlinTestUtils.getHomeDirectory()
|
||||
}
|
||||
+197
@@ -0,0 +1,197 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.android.intention;
|
||||
|
||||
import com.intellij.testFramework.TestDataPath;
|
||||
import org.jetbrains.kotlin.test.JUnit3RunnerWithInners;
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils;
|
||||
import org.jetbrains.kotlin.test.TargetBackend;
|
||||
import org.jetbrains.kotlin.test.TestMetadata;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */
|
||||
@SuppressWarnings("all")
|
||||
@TestMetadata("idea/testData/android/intention")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public class AndroidIntentionTestGenerated extends AbstractAndroidIntentionTest {
|
||||
public void testAllFilesPresentInIntention() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/android/intention"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), TargetBackend.ANY, true);
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/android/intention/implementParcelable")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class ImplementParcelable extends AbstractAndroidIntentionTest {
|
||||
public void testAllFilesPresentInImplementParcelable() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/android/intention/implementParcelable"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), TargetBackend.ANY, true);
|
||||
}
|
||||
|
||||
@TestMetadata("allNullableTypes.kt")
|
||||
public void testAllNullableTypes() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/android/intention/implementParcelable/allNullableTypes.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("allTypes.kt")
|
||||
public void testAllTypes() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/android/intention/implementParcelable/allTypes.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("alreadyImplemented.kt")
|
||||
public void testAlreadyImplemented() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/android/intention/implementParcelable/alreadyImplemented.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("constructorProperties.kt")
|
||||
public void testConstructorProperties() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/android/intention/implementParcelable/constructorProperties.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("constructorPropertiesWithParameters.kt")
|
||||
public void testConstructorPropertiesWithParameters() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/android/intention/implementParcelable/constructorPropertiesWithParameters.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("missingConstructor.kt")
|
||||
public void testMissingConstructor() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/android/intention/implementParcelable/missingConstructor.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("missingDescribeContents.kt")
|
||||
public void testMissingDescribeContents() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/android/intention/implementParcelable/missingDescribeContents.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("missingWriteToParcel.kt")
|
||||
public void testMissingWriteToParcel() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/android/intention/implementParcelable/missingWriteToParcel.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("simple.kt")
|
||||
public void testSimple() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/android/intention/implementParcelable/simple.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("withTransient.kt")
|
||||
public void testWithTransient() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/android/intention/implementParcelable/withTransient.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/android/intention/redoParcelable")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class RedoParcelable extends AbstractAndroidIntentionTest {
|
||||
public void testAllFilesPresentInRedoParcelable() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/android/intention/redoParcelable"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), TargetBackend.ANY, true);
|
||||
}
|
||||
|
||||
@TestMetadata("indirectParcelable.kt")
|
||||
public void testIndirectParcelable() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/android/intention/redoParcelable/indirectParcelable.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("init.kt")
|
||||
public void testInit() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/android/intention/redoParcelable/init.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("initWithExtraStatements.kt")
|
||||
public void testInitWithExtraStatements() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/android/intention/redoParcelable/initWithExtraStatements.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("noParcelable.kt")
|
||||
public void testNoParcelable() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/android/intention/redoParcelable/noParcelable.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("oldField.kt")
|
||||
public void testOldField() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/android/intention/redoParcelable/oldField.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("oldFieldSecondary.kt")
|
||||
public void testOldFieldSecondary() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/android/intention/redoParcelable/oldFieldSecondary.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("simple.kt")
|
||||
public void testSimple() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/android/intention/redoParcelable/simple.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/android/intention/removeParcelable")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class RemoveParcelable extends AbstractAndroidIntentionTest {
|
||||
public void testAllFilesPresentInRemoveParcelable() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/android/intention/removeParcelable"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), TargetBackend.ANY, true);
|
||||
}
|
||||
|
||||
@TestMetadata("inderectParcelable.kt")
|
||||
public void testInderectParcelable() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/android/intention/removeParcelable/inderectParcelable.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("init.kt")
|
||||
public void testInit() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/android/intention/removeParcelable/init.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("initWithExtraStatements.kt")
|
||||
public void testInitWithExtraStatements() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/android/intention/removeParcelable/initWithExtraStatements.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("noParcelable.kt")
|
||||
public void testNoParcelable() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/android/intention/removeParcelable/noParcelable.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("simple.kt")
|
||||
public void testSimple() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/android/intention/removeParcelable/simple.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -58,7 +58,7 @@ abstract class AbstractAndroidLintQuickfixTest : KotlinAndroidTestCase() {
|
||||
myFixture.checkResultByFile(path + ".expected")
|
||||
}
|
||||
else {
|
||||
assertNull("Intention should not be availabale", myFixture.availableIntentions.find { it.text == intentionText })
|
||||
assertNull("Intention should not be available", myFixture.availableIntentions.find { it.text == intentionText })
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+21
@@ -36,6 +36,27 @@ public class AndroidLintQuickfixTestGenerated extends AbstractAndroidLintQuickfi
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/android/lintQuickfix"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), TargetBackend.ANY, true);
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/android/lintQuickfix/parcelable")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class Parcelable extends AbstractAndroidLintQuickfixTest {
|
||||
public void testAllFilesPresentInParcelable() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/android/lintQuickfix/parcelable"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), TargetBackend.ANY, true);
|
||||
}
|
||||
|
||||
@TestMetadata("missingCreator.kt")
|
||||
public void testMissingCreator() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/android/lintQuickfix/parcelable/missingCreator.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("noImplementation.kt")
|
||||
public void testNoImplementation() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/android/lintQuickfix/parcelable/noImplementation.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/android/lintQuickfix/requiresApi")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
Reference in New Issue
Block a user