Need to check sub class (of Class type) if it inherits from super class, using custom Detector via method visitCallExpression(UCallExpression) - intellij-plugin

I have created my own lint Detector.visitCallExpression(UCallExpression) and I need to find a way to check if a MyClass class parameter passed into a method call is a child of MyParent class?
//Example having this below code somewhere to be Lint scanned.
someObject.method(MyClass.class)
How can I determine MyClass.class inherits from MyParent class?
//Using the IntelliJ InheritanceUtil utility class
//Converts argument of MyClass.class -> psiClass
InheritanceUtil.isInheritor(psiClass, "com.somePackage.MyParent")
The PsiClass I get from the MyClass.class parameter, is resolved to the base java.lang.Class object, so the InheritanceUtil check always return false~

Anyway i found the solution
/**
* Detects if a Class<?> => PsiClass:Class<?> is a subclass of a PsiClass(?)
*
* #param type The PsiType object that is a PsiClass of the class to be checked for subclass
* #param compareToParentCanonicalClass The Class canonical name to be compared as a super/parent class
*
* #return true if the PsiType is a subclass of compareToParentCanonicalClass, false otherwise
*/
open fun isPsiTypeSubClassOfParentClassType(type: PsiType, compareToParentCanonicalClass: String): Boolean {
println("superClass checking:$type")
var psiClss = PsiTypesUtil.getPsiClass(type)
val pct = type as PsiClassType
val psiTypes: List<PsiType> = ArrayList<PsiType>(pct.resolveGenerics().substitutor.substitutionMap.values)
for (i in psiTypes.indices) {
println("canonical:"+ psiTypes[i].canonicalText)
var psiClass = psiClss?.let { JavaPsiFacade.getInstance(it.project).findClass(psiTypes[i].canonicalText, psiClss.resolveScope) }
return InheritanceUtil.isInheritor(psiClass, compareToParentCanonicalClass)
}
return false;
}

Related

Subclass Parameter Name Overriding Superclass Val

Experienced with Java, but fairly new to Kotlin.
When the subclass param has same name as a superclass val... Android Studio does not throw validation error stating need for #Override annotation. However, attempting to access name from within Business references the param name rather than the superclass val (which feels like an override to me).
class Business(
val name: String
) {
// ...
}
class FirstBusiness(name: String) : Business(name) {
val test = name; // name referencing param name rather than super's name
}
Of course, I can just name the param something different, but I really just want to pass the name to the superclass... otherwise excluding any storage of it in FirstBusiness.
Am I overlooking something? I'm surprised that even if I don't declare FirstBusiness param name as a val/var, it seems to be overriding Business.name. I'm assuming the param isn't truly overriding the super val as the IDE isn't complaining... but why is the param the only suggestion instead of the super val?
Edit: I do notice different (more expected from my Java experience) behavior if I do the param-passing outside of the primary constructor design like so...
class FirstBusiness : Business {
constructor(name: String) : super(name)
fun thing() {
val v = name // now references super's name
}
}
Thank you!
Just like how you would do it in Java if you have shadowed the name of a superclass's field, you can clarify it with the super keyword.
class FirstBusiness(name: String) : Business(name) {
val test = super.name
}
In your case, it's not overriding the superclass's property. What's happening is that property initializers at the property declaration sites are considered part of the primary constructor's initialization block, so the constructor parameter is closer in scope than the superclass's property.
Suppose for a moment that these classes were defined in Java, and in the superclass you simply used a field instead of a getter:
public class Business {
public String name;
public Business(String name) {
this.name = name;
}
}
Then your code where you initialize your property at its declaration site is just like initializing a field from a constructor, like this in Java:
public class FirstBusiness extends Business {
private String test;
public FirstBusiness(String name) {
super(name);
this.test = name; // It's using the parameter, not the superclass's
// property, but the superclass property isn't overridden.
}
}

KotlinPoet: Add function to existing class

I want to build an annotation processor that generates a public "non-mutable class" getter function of a private "mutable class" field (e.g. returning a LiveData version of a MutableLiveData field).
What I want to write:
class MyClass {
#WithGetNonMutable
private val popup: MutableLiveData<PopupTO?> = MutableLiveData()
}
What I want to generate
class MyClass {
private val popup: MutableLiveData<PopupTO?> = MutableLiveData()
fun getPopup(): LiveData<PopupTO?> = popup
}
Generating the function with the correct return type is no problem:
val liveDataType = ClassName("android.arch.lifecycle", "LiveData")
val returnType = liveDataType.parameterizedBy(genericDataType)
val function = FunSpec.builder("get${element.simpleName}")
.addModifiers(KModifier.PUBLIC)
.addStatement("return ${element.simpleName}")
.returns(returnType)
.build()
The problem is that the variable (popup) is private - so to access it my generated function also needs to be part of that class (it can't be a simple extension function in a new file). The KotlinPoet example all write to new files - but there's no way to access the private field (or is there?) so I'd need to write the function in the actual class file? How can I achieve this?
Annotation Processors cannot modify existing code, they can only generate new code.
That said, you could maybe modify your approach and generate an extension function instead of a member function.
Keep your MyClass (with private modifier changed to internal):
class MyClass {
#WithGetNonMutable
internal val popup: MutableLiveData<PopupTO?> = MutableLiveData()
}
Generate a new file (within the same package), with the following contents:
fun MyClass.getPopup(): LiveData<PopupTO?> = this.popup
If you completely can't modify the MyClass (you can't change private to internal), you can (it's not that elegant, but you can do it):
In the generated extension function use Reflection to access a private field.

Extension fields in Kotlin

It's easy to write extension methods in Kotlin:
class A { }
class B {
fun A.newFunction() { ... }
}
But is there some way to create extension variable? Like:
class B {
var A.someCounter: Int = 0
}
You can create an extension property with overridden getter and setter:
var A.someProperty: Int
get() = /* return something */
set(value) { /* do something */ }
But you cannot create an extension property with a backing field because you cannot add a field to an existing class.
No - the documentation explains this:
Extensions do not actually modify classes they extend. By defining an extension, you do not insert new members into a class, but merely make new functions callable with the dot-notation on instances of this class.
and
Note that, since extensions do not actually insert members into classes, there’s no efficient way for an extension property to have a backing field. This is why initializers are not allowed for extension properties. Their behavior can only be defined by explicitly providing getters/setters.
Thinking about extension functions/properties as just syntactic sugar for calling a static function and passing in a value hopefully makes this clear.
However, if you really, really want to do something like this...
As stated above regarding efficiency, an additional backing field added directly to the class is the best way to store data non-derivable from existing non-private members from the class. However, if you don't control the implementation of the class and are dead-set on creating a new property that can store new data, it can be done in a way that is not abysmally inefficient by using separate external tables. Use a separate map that keys on object instances of this class with values that map directly to the value you want to add then define an extension getter and/or setter for this property which uses your external table to store the data associated with each instance.
val externalMap = mutableMapOf<ExistingClass, Int>()
var ExistingClass.newExtensionProperty : Int
get() = externalMap[this] ?: 0
set(value:Int) { externalMap[this] = value }
The additional map lookups will cost you - and you need to consider memory leaks, or using appropriately GC-aware types, but it does work.
There's no way to add extension properties with backing fields to classes, because extensions do not actually modify a class.
You can only define an extension property with custom getter (and setter for var) or a delegated property.
However, if you need to define an extension property which would behave as if it had a backing field, delegated properties come in handy.
The idea is to create a property delegate that would store the object-to-value mapping:
using the identity, not equals()/hashCode(), to actually store values for each object, like IdentityHashMap does;
not preventing the key objects from being garbage collected (using weak references), like WeakHashMap does.
Unfortunately, there is no WeakIdentityHashMap in JDK, so you have to implement your own (or take a complete implementation).
Then, based on this mapping you can create a delegate class satisfying the property delegates requirements. Here's an example non-thread-safe implementation:
class FieldProperty<R, T : Any>(
val initializer: (R) -> T = { throw IllegalStateException("Not initialized.") }
) {
private val map = WeakIdentityHashMap<R, T>()
operator fun getValue(thisRef: R, property: KProperty<*>): T =
map[thisRef] ?: setValue(thisRef, property, initializer(thisRef))
operator fun setValue(thisRef: R, property: KProperty<*>, value: T): T {
map[thisRef] = value
return value
}
}
Usage example:
var Int.tag: String by FieldProperty { "$it" }
fun main(args: Array<String>) {
val x = 0
println(x.tag) // 0
val z = 1
println(z.tag) // 1
x.tag = "my tag"
z.tag = x.tag
println(z.tag) // my tag
}
When defined inside a class, the mapping can be stored independently for instances of the class or in a shared delegate object:
private val bATag = FieldProperty<Int, String> { "$it" }
class B() {
var A.someCounter: Int by FieldProperty { 0 } // independent for each instance of B
var A.tag: String by bATag // shared between the instances, but usable only inside B
}
Also, please note that identity is not guaranteed for Java's primitive types due to boxing.
And I suspect the performance of this solution to be significantly worse than that of regular fields, most probably close to normal Map, but that needs further testing.
For nullable properties support and thread-safe implementation please refer to here.
You can't add a field, but you can add a property, that delegates to other properties/methods of the object to implement its accessor(s). For example suppose you want to add a secondsSinceEpoch property to the java.util.Date class, you can write
var Date.secondsSinceEpoch: Long
get() = this.time / 1000
set(value) {
this.time = value * 1000
}
If you are extending View you can do it quite easily like this...
This is example how I create some my custom class Event property in EditText class extension:
Define id for key :
<?xml version="1.0" encoding="utf-8"?>
<resources>
<item name="EditTextEventOnClearTagKey" type="id" />
</resources>
Define one reusable extension like this:
fun <T : Any> View.tagProperty(#IdRes key: Int, onCreate: () -> T): T {
#Suppress("UNCHECKED_CAST")
var value = getTag(key) as? T
if (value.isNull) {
value = onCreate()
setTag(key, value)
}
return value!!
}
Use it in wherever View extension you need:
val EditText.eventClear get() = tagProperty(R.id.EditTextEventOnClearTagKey) { event<Unit>() }

How are overridden properties handled in init blocks?

I'm trying to understand why the following code throws:
open class Base(open val input: String) {
lateinit var derived: String
init {
derived = input.toUpperCase() // throws!
}
}
class Sub(override val input: String) : Base(input)
When invoking this code like this:
println(Sub("test").derived)
it throws an exception, because at the time toUpperCase is called, input resolves to null. I find this counter intuitive: I pass a non-null value to the primary constructor, yet in the init block of the super class it resolves to null?
I think I have a vague idea of what might be going on: since input serves both as a constructor argument as well as a property, the assignment internally calls this.input, but this isn't fully initialized yet. It's really odd: in the IntelliJ debugger, input resolves normally (to the value "test"), but as soon as I invoke the expression evaluation window and inspect input manually, it's suddenly null.
Assuming this is expected behavior, what do you recommend to do instead, i.e. when one needs to initialize fields derived from properties of the same class?
UPDATE:
I've posted two even more concise code snippets that illustrate where the confusion stems from:
https://gist.github.com/mttkay/9fbb0ddf72f471465afc
https://gist.github.com/mttkay/5dc9bde1006b70e1e8ba
The original example is equivalent to the following Java program:
class Base {
private String input;
private String derived;
Base(String input) {
this.input = input;
this.derived = getInput().toUpperCase(); // Initializes derived by calling an overridden method
}
public String getInput() {
return input;
}
}
class Derived extends Base {
private String input;
public Derived(String input) {
super(input); // Calls the superclass constructor, which tries to initialize derived
this.input = input; // Initializes the subclass field
}
#Override
public String getInput() {
return input; // Returns the value of the subclass field
}
}
The getInput() method is overridden in the Sub class, so the code calls Sub.getInput(). At this time, the constructor of the Sub class has not executed, so the backing field holding the value of Sub.input is still null. This is not a bug in Kotlin; you can easily run into the same problem in pure Java code.
The fix is to not override the property. (I've seen your comment, but this doesn't really explain why you think you need to override it.)
The confusion comes from the fact that you created two storages for the input value (fields in JVM). One is in base class, one in derived. When you are reading input value in base class, it calls virtual getInput method under the hood. getInput is overridden in derived class to return its own stored value, which is not initialised before base constructor is called. This is typical "virtual call in constructor" problem.
If you change derived class to actually use property of super type, everything is fine again.
class Sub(input: String) : Base(input) {
override val input : String
get() = super.input
}

Why can't I access private class methods in the class's companion object in Scala?

I'm working on a homework assignment for my object oriented design class, and I'm running into trouble with Scala's companion objects. I've read in a few places that companion objects are supposed to have access to their companion class's private methods, but I can't seem to get it to work. (Just as a note, the meat of the assignment had to do with implementing a binary search tree, so I'm not just asking for answers...)
I have an object that is supposed to create an instance of my private class, BstAtlas (Bst is also defined in the Atlas object, took it out for clarity):
object Atlas {
def focusRoom(newRoom:Room,a:Atlas):Atlas = a.helpFocusRoom(newRoom);
abstract class Atlas {
...
protected def helpFocusRoom(n:Room):Atlas;
...
}
private class BstAtlas(bst:Bst) extends Atlas {
...
protected def helpFocusRoom(newRoom:Room):Atlas = ...
// uses some of bst's methods
...
}
}
But when I go to compile, I get the following error:
Question23.scala:15: error: method
helpFocusRoom cannot be accessed in
Atlas.Atlas
a.helpFocusRoom(newRoom);
The function helpFocusRoom needs to be hidden, but I don't know how to hide it and still have access to it inside of the companion object.
Can anyone tell me what I'm doing wrong here?
The problem is that classes and companion objects can't be nested like that. To define a companion object, you need to define the class outside of the object's body but in the same file.
Companion objects should be next to their real object, not containing it:
object Example {
class C(val i: Int = C.DefaultI) { }
object C { protected val DefaultI = 5 }
}
scala> (new Example.C).i
res0: Int = 5
scala> Example.C.DefaultI
<console>:11: error: value DefaultI cannot be accessed in object Example.C
Example.C.DefaultI
Alternatively, you can alter the scope of the protected keyword to include the enclosing object:
object Example {
def value = (new D).hidden
class D(val i: Int = 5) {
protected[Example] def hidden = i*i
}
}
scala> Example.value
res1: Int = 25
but here you ought not name the outer object the same thing as the inner class or you'll have trouble referring to it from within the class.