Kotlin Reflection - Check if property has type - reflection

I want to iterate over all fields in one of my classes, filter for annotated ones and then check if the field has one specific type.
All I found was field.returnType.isSubtype(other: KType) but I don't know how to get the KType of my other class.
Here is my code so far:
target.declaredMemberProperties.forEach {
if (it.findAnnotation<FromOwner>() != null) {
if ( /* it.returnType is Component <- Here I need some working check */ ) {
// do stuff
} else {
// do ther stuff
}
}
}

There are at least two solutions here:
Get the KClass<*> of it.returnType using .jvmErasure, then check the subtype relationship for the KClasses:
it.returnType.jvmErasure.isSubclassOf(Component::class)
Since Kotlin 1.1, you can construct the KType from the KClass token using .createType() (check its optional parameters: you can use them to provide nullability info, type arguments and annotations), and then check the subtype as you suggested:
it.returnType.isSubtypeOf(Component::class.createType())
Creating the type on every iteration may introduce performance issues, make sure you cache it if you need it often.

Related

Kotlin reflection on object instance

I've been trying some stuff from kotlin.reflection during my project, and got stuck on something what occurs to me as hard to understand, I have declared object as follows:
object WebsiteMapping
{
const val ADMIN = "/admin"
}
once I call:
Arrays
.stream(WebsiteMapping::class.java.declaredFields)
.forEach { field -> println(field.type) }
what I get is:
class java.lang.String
class mapping.WebsiteMapping
When I looked a little bit into what is behind declaredFields invocation I grasped why it works as it is, but is there any convenient way of taking only declared consts within that object without getting also root of the whole structure?
The field with the type class mapping.WebsiteMapping is, basically, not the root of the structure but a special field generated in the object type that holds the reference to the singleton object.
In Kotlin, this field is named INSTANCE by convention. You can therefore filter the fields that you get from the class as follows:
WebsiteMapping::class.java.declaredFields
.filter { it.name != "INSTANCE" }
.forEach { println(it.type) }
Another solution is to switch from java.reflect.* to the Kotlin reflection API kotlin.reflect (needs a dependency on the kotlin-reflect module), which automatically filters the property:
WebsiteMapping::class.memberProperties
.forEach { println(it.returnType) }

How can advance google closure compilation be used with ES6 classes and arbitrary defineProperty?

I maintain a data flow library that allows programmers to define new properties during instantiation, then does neat things at run-time with both property reads and writes, all transparently thanks to JS defineProperty. Sample usage, where TagSession is defined with the ES6 class keyword:
const sithApp = new TagSession( null, 'SithTrakSession',
{
obiTrakker: cF( c => new WebSocket('ws://localhost:4000')
.onmessage = msg => c.md.obiLoc = JSON.parse(msg.data)),
obiLoc: cI( null),
sithIds: cI([-1,-2,3616,-3,-4])
});
I can now write code where the map keywords are transparent accessors:
function SithTrak () {
return div({class: "app-container"},
h1({
class: "css-planet-monitor",
content: cF(c => "Obi-Wan currently on " +
(sithApp.obiLoc ?
sithApp.obiLoc.name : "...dunno"))
}))
}
This works great uncompiled and with Google Closure SIMPLE_OPTIMIZATION, but ADVANCED_COMPILATION warns (and the output fails) about, eg:
WARNING - Property obiLoc never defined on TagSession
withObi: cF( c=> c.md.info && sithApp.obiLoc
I have looked at all the annotations that might apply, but nothing seems suited to such a dynamic capability.
Am I missing something obvious, or is this combo of dynamism and optimization asking too much?
Dynamic properties added with this method would require using a bracket access for ADVANCED mode: sithApp['obiLoc']. In ADVANCED mode, the compiler must know about all properties accessed via the dot nation at compile time.
Since it isn't known that these properties are defined on the class you are going to get type warnings, bit it shouldn't break your code.
You can add declarations to silence the type warnings:
/** #type {?} */
TagSession.prototype.objLoc;
In other cases, you might be able to use #lends but I don't think this will work here as the types provided might not match the expected type of the property value. But there isn't enough context to be sure:
/** #lends {TagSession.prototype} */ ({
obiTrakker: ...,
obiLoc: ...,
sithIds: ...
})

Typescript reflection - required parameters & default values

In short: is there a way to know if a typescript parameter is required and/or has a default value?
Longer version:
Say I have the following file:
//Foo.ts
class Bar {
foo(required:string,defaultValue:number=0,optional?:boolean) {
...
}
}
I would like to know of each of the parameters:
the name
the type
is it required?
does it have a default value?
I have succesfully used method decorators with the TypeScript reflection API to get the types of the parameters, I've used this method to get their names, but so far I have not found a way to know if a variable is required and/or has a default value.
I know the typescript compiler itself can be used from within typescript. So I'm wondering if there is a way to use the parse tree of the compiler to see if a parameter is required and/or has a default value?
How would that work?
If you want to do this from scratch...
On a high level, one way of doing it is to:
Figure out how to get the SourceFile node using the compiler api of your file. That requires a bit of an explanation in itself.
From there, use the api's forEachChild function to loop over all the nodes in the file and find the node with a kind of SyntaxKind.ClassDeclaration and .name property with text Bar.
Then loop over all the children of the class by again using the api's forEachChild function and get the ones that has the kind SyntaxKind.MethodDeclaration and .name property with text foo.
To get the parameters, you will need to loop over the method node's parameters property.
Then for each parameter node, to get the name you can call .getText() on the .name property.
You can tell if the parameter is optional by doing:
const parameterDeclaration = parameterNode as ts.ParameterDeclaration;
const isOptional = parameterDeclaration.questionToken != null || parameterDeclaration.initializer != null || parameterDeclaration.dotDotDotToken != null;
Or you could use the TypeChecker's isOptionalParameter method.
To get its default expression, you will just have to check the initializer property:
propertyDeclaration.initializer;
To get the type use the TypeChecker's getTypeOfSymbolAtLocation method and pass in the symbol of the node... that gets a little bit complicated so I won't bother explaining it (think about how it's different with union types and such).
Don't do it from scratch...
I've created a wrapper around the TypeScript compiler api. Just use this code with ts-simple-ast (edit: Previously this talked about my old ts-type-info library, but ts-simple-ast is much better):
import { Project } from "ts-morph";
// read more about setup here:
// https://ts-morph.com/setup/adding-source-files
const project = new Project({ tsConfigFilePath: "tsconfig.json" });
const sourceFile = project.getSourceFileOrThrow("src/Foo.ts");
const method = sourceFile.getClassOrThrow("Bar").getInstanceMethodOrThrow("foo");
Once you have the method, it's very straightforward to get all the information you need from its parameters:
console.log(method.getName()); // foo
for (const param of method.getParameters()) {
console.log(param.getName());
console.log(param.getType().getText());
console.log(param.isOptional());
console.log(param.getInitializer() != null);
}

Does Objective-c require you to have different classes for every instance of UIPickerViews

I have three different search pages all running off of my Search View Controller. Each one has a different search method, but the screens are basically the same. selections from the picker will fill in the text fields and the lat and long is the device's which is constantly updated and shown in those fields. My question is, to adequately add the correct data and to comply with the UIPickerViewDelegate do I need to add three more classes to run those picker views or is it allowable to do the work in the SearchViewController class to define the PickerView?
It is allowable (and probably preferable) to make the SearchViewController the delegate of each separate UIPickverView. Remember to include the <UIPickerViewDelegate> annotation on your SearchViewController's interface (like #interface SearchViewController : UIViewController <UIPickerViewDelegate>) and the compiler will helpfully remind you if you're missing any required methods; however as of 6.1 there are no required methods in the UIPickerViewDelegate protocol.
Note that every method in the UIPickerViewDelegate protocol takes a UIPickerView as its first argument. Check that argument to see if it is equal to each of your three separate UIPickerViews (and it should certainly be equal to one of them!) and modify the behavior of the delegate methods as needed for each view. You'll end up with code that like this:
- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component {
if (pickerView == self.myFirstPickerView) {
return self.myFirstSearchMethodResultTitles[row];
} else if (pickerView == self.mySecondPickerView) {
return self.mySecondSearchMethodResultTitles[row];
} else if (pickerView == self.myThirdPickerView) {
return self.myThirdSearchMethodResultTitles[row];
} else {
NSAssert(NO, #"Should not have reached this point!");
return nil;
}
}

How to force Newtonsoft.Json.Net Dictionary to serialize as KeyValuePair

Is it possible to get Json.Net 4.5+ to serialize my Dictionary<,> as this;
{
dic:[
{k:"apples",v:2},
{k:"pears",v:43}
]
}
as opposed to;
{
dic:{
"apples":2,
"pears":43,
}
}
Whilst the latter is a great default, and looking at the SO posts almost everyone wanted this when it wasn't the default, but I have one specific case when I actually want it the other (former) way instead. I tried adding the following to the dictionary class or member;
[JsonProperty(ItemConverterType=typeof(DictionaryArrayConverter)]
But that just causes it to barf on a null in the serialization stack.

Resources