In Dart, can you retrieve metadata (e.g., annotations) at runtime using reflection? - annotations

If so, how is this accomplished? If not, are there any plans to support this in future Dart releases? I'm mostly referring to your own created custom annotations.
In this documentation link, https://www.dartlang.org/docs/spec/latest/dart-language-specification.html#h.d0rowtffuudf, it says: "Metadata is associated with the abstract syntax tree of the program construct p that immediately follows the metadata, assuming p is not itself metadata or a comment . Metadata can be retrieved at runtime via a reflective call, provided the annotated program construct p is accessible via reflection.
Reflective access to metadata is not yet implemented as of the M3 release."
Thank you.

Sample code for understanding.
import "dart:mirrors";
void main() {
var object = new Class1();
var classMirror = reflectClass(object.runtimeType);
// Retrieve 'HelloMetadata' for 'object'
HelloMetadata hello = getAnnotation(classMirror, HelloMetadata);
print("'HelloMetadata' for object: $hello");
// Retrieve 'Goodbye' for 'object.method'
var methodMirror = (reflect(object.method) as ClosureMirror).function;
Goodbye goodbye = getAnnotation(methodMirror, Goodbye);
print("'Goodbye' for object: $goodbye");
// Retrieve all 'Goodbye' for 'object.method'
List<Goodbye> goodbyes = getAnnotations(methodMirror, Goodbye);
print("'Goodbye's for object.method': $goodbyes");
// Retrieve all metadata for 'object.method'
List all = getAnnotations(methodMirror);
print("'Metadata for object.method': $all");
}
Object getAnnotation(DeclarationMirror declaration, Type annotation) {
for (var instance in declaration.metadata) {
if (instance.hasReflectee) {
var reflectee = instance.reflectee;
if (reflectee.runtimeType == annotation) {
return reflectee;
}
}
}
return null;
}
List getAnnotations(DeclarationMirror declaration, [Type annotation]) {
var result = [];
for (var instance in declaration.metadata) {
if (instance.hasReflectee) {
var reflectee = instance.reflectee;
if (annotation == null) {
result.add(reflectee);
} else if (reflectee.runtimeType == annotation) {
result.add(reflectee);
}
}
}
return result;
}
#HelloMetadata("Class1")
class Class1 {
#HelloMetadata("method")
#Goodbye("method")
#Goodbye("Class1")
void method() {
}
}
class HelloMetadata {
final String text;
const HelloMetadata(this.text);
String toString() => "Hello '$text'";
}
class Goodbye {
final String text;
const Goodbye(this.text);
String toString() => "Goodbye '$text'";
}
Output:
'HelloMetadata' for object: Hello 'Class1'
'Goodbye' for object: Goodbye 'method'
'Goodbye's for object.method': [Goodbye 'method', Goodbye 'Class1']
'Metadata for object.method': [Hello 'method', Goodbye 'method', Goodbye 'Class1']
P.S.
If Dart had supported the generic methods that I would recommend to use this code.
T getAnnotation<T>(DeclarationMirror declaration) {
for (var instance in declaration.metadata) {
if (instance.hasReflectee) {
var reflectee = instance.reflectee;
if (reflectee.runtimeType == T) {
return reflectee;
}
}
}
return null;
}
And retrieve metadata with generic method.
var goodbye = getAnnotation<Goodbye>(methodMirror);

Yes you can retrieve annotations with dart:mirrors :
import 'dart:mirrors';
#override
class A {}
main(){
TypeMirror typeOfA = reflectType(A);
// or reflectType(a.runtimeType) if a is an instance of A
// getting metadata of the class
List<InstanceMirror> metadatas = typeOfA.metadata;
for (InstanceMirror m in metadatas) {
ClassMirror cm = m.type;
// here you get the Class of the annotation
}
}

Related

swashbuckle openapi 3 write example and description for the dynamically generated model classes

My model properties definition is coming from a json file so using reflection to write the classes to be shown under schema on resulting swagger page.
foreach (var model in Models)
{
if (!ModelTypes.ContainsKey(model.Key))
{
anyNonCompiledModel = true;
BuildModelCodeClass(modelComponentBuilder, model.Value);//Build model classes
}
}
BuildModelCodeEnd(modelComponentBuilder);
if (anyNonCompiledModel)
{
CSharpCompiler compiler = new CSharpCompiler();
compiler.AddReference(typeof(object));
compiler.AddReference(typeof(ResourceFactory));
compiler.AddReference(typeof(System.Runtime.Serialization.DataContractResolver));
compiler.AddReference(typeof(System.Runtime.Serialization.DataContractAttribute));
var types = compiler.Compiler(modelComponentBuilder.ToString()); //write model classes
foreach (var type in types)
{
ModelTypes.Add(type.Name, type);
}
}
public void BuildModelCodeClass(StringBuilder modelComponentBuilder, MetadataModelEntity model)
{
modelComponentBuilder.AppendLine($"public class {model.Name} {{");
foreach (var p in model.Data.Properties)
{
if (p.Obsoleted) continue;
if (p.Type.Type == "array")
{
modelComponentBuilder.AppendLine($" public {p.Type.ArrayType.ObjectName}[] {p.Name} {{get;set;}}");
}
else
{
//primitive types
modelComponentBuilder.AppendLine($" public {p.Type.ObjectName} {p.Name} {{get;set;}}");
}
}
modelComponentBuilder.AppendLine(
#"}
");
}
If i provide the description and example like following (in BuildModelCodeClass, inside the loop) then the example and description displays for me.
if (!string.IsNullOrWhiteSpace((string)p.Example))
{
modelComponentBuilder.AppendLine($" ///<example>{p.Example}</example>");
}
if (!string.IsNullOrWhiteSpace((string)p.Description))
{
modelComponentBuilder.AppendLine($" ///<description>{p.Description}</description>");
}
However, i dont want to do above.
I want to write my models via the open api and not via the C# Compiler, is it possible?
I want to show example and description via schema (may be under paths some where). How can i do this? Context has my models info available that i can interact with here.
public class SwaggerDocumentFilter : IDocumentFilter
{
SwaggerDocument _swaggerDocument;
public SwaggerDocumentFilter(object apiConfigure)
{
_swaggerDocument = ((ApiGatewayConfiguration)apiConfigure).SwaggerDocument;
}
public void Apply(OpenApiDocument document, DocumentFilterContext context)
{
if (document.Info.Extensions == null || !document.Info.Extensions.ContainsKey(SwaggerEndpoint.ExtensionDocName)) return;
var openIdString = document.Info.Extensions[SwaggerEndpoint.ExtensionDocName] as OpenApiString;
if (openIdString == null) return;
var docName = openIdString.Value;
SwaggerEndpoint endpoint = _swaggerDocument.SwaggerEndpoints.SingleOrDefault(x => x.Name == docName);
if (endpoint == null) return;
//Add server objects
document.Servers = endpoint.ServerObjects;
//Add Tags objects
document.Tags = endpoint.Tags;
//Set swagger paths objects
var pathsObjects = _swaggerDocument.GetPathsObject(docName, context);
if (pathsObjects.IsValid())
{
pathsObjects.ToList().ForEach(
item => document.Paths.Add(item.Key, item.Value)
);
}
//Add Schema components
//Add Example/Examples
}
}
Following helped
https://github.com/domaindrivendev/Swashbuckle.WebApi/issues/162
AddSchemaExamples.cs
public class AddSchemaExamples : ISchemaFilter
{
public void Apply(Schema schema, SchemaRegistry schemaRegistry, Type type)
{
if (type == typeof(Product))
{
schema.example = new Product
{
Id = 123,
Type = ProductType.Book,
Description = "Treasure Island",
UnitPrice = 10.0M
};
}
}
}
SwaggerConfig.cs
httpConfig
.EnableSwagger(c =>
{
c.SchemaFilter<AddSchemaExamples>()
});
My implementation for the Apply since model is dynamic
if (model != null)
{
schema.Description = model.Description;
foreach (var p in schema.Properties)
{
var mp = model.Data.Properties.SingleOrDefault(x => x.Name == p.Key);
if (mp != null)
{
if (!string.IsNullOrWhiteSpace(mp.Description))
{
p.Value.Description = mp.Description;
}
if(!string.IsNullOrWhiteSpace(mp.Example))
{
p.Value.Example =
new Microsoft.OpenApi.Any.OpenApiString(mp.Example.ToString());
}
}
}
}

How do i invoke method on a generic type in dart

There are several models have a same structure, { type: xxx, settings: xxx}, so i would like to use a parent class "WidgetConfig" with a generic type "T" to implement this, but problem occurs when i add "fromJson" methods. How can i invoke method on a generic type or any other ways to implement this?
class BannerWidgetViewModel extends ChangeNotifier {
WidgetConfig<BannerWidgetConfig> config;
BannerWidgetViewModel(String configJson){
config = WidgetConfig.fromJson(configJson);
}
}
class BannerWidgetConfig {
String imgUrl;
String padImgUrl;
String lessonId;
BannerWidgetConfig.fromJson(json){
if (json != null) {
this.imgUrl = json['imgUrl'];
this.padImgUrl = json['padImgUrl'];
this.lessonId = json['lessonId'];
}
}
}
class WidgetConfig<T> {
WidgetType type;
WidgetConfig.fromJson(json){
if (json != null) {
this.type = json['type'];
// this.settings = T.fromJson(json['settings']); // T doesn't have fromJson method
}
}
}
then i use a abstract class but still not working.
abstract class BaseWidgetConfig {
BaseWidgetConfig.fromJson(dynamic json);
}
class WidgetConfig<T extends BaseWidgetConfig> {
WidgetType type;
T settings;
WidgetConfig.fromJson(json){
if (json != null) {
this.type = json['type'];
this.settings = T.fromJson();
}
}
}
code picture
Directly show the function as a reference.
send function as a reference here.
FireStoreHelper.getList<ModelLesson>('grade4', ModelLesson.fromJson);
get the method here.
static Future<List<T>> getList<T>(String path, Function fromJson)

Assign property from external object

I am trying to set a property from an external object:
main() {
A a = A();
print(a.prop.runtimeType); // is 'Null'
MiddleMan().b.assign(a.prop);
print(a.prop.runtimeType); // should be 'B', but is 'Null'
}
class A { B prop; }
class MiddleMan { B b = B(); }
class B {
assign(B property) { property = this; }
}
I managed to achieve this functionality with mirrors:
import 'dart:mirrors';
main() {
A a = A();
print(a.prop.runtimeType); // is 'Null'
MiddleMan().b.assign(a, 'prop');
print(a.prop.runtimeType); // is 'B'
}
class A { B prop; }
class MiddleMan { B b = B(); }
class B {
assign(dynamic object, String property) {
InstanceMirror reflectee = reflect(object);
reflectee.setField(Symbol(property), this);
}
}
But then I realized mirrors are disabled in Flutter. Is there any workaround?
Just as mentioned here, one can use a wrapper class:
main() {
A a = new A();
print(a.prop.value.runtimeType); // is 'Null'
MiddleMan().b.assign(a.prop);
print(a.prop.value.runtimeType); // is 'B'
}
class A {
Wrapper<B> prop = Wrapper(null);
}
class MiddleMan {
B b = B();
}
class B {
assign(Wrapper<B> property) {
property.value = this;
}
}
class Wrapper<T> {
T value;
Wrapper(this.value);
}
But because my intention is to write a library and make its use easy for the user, with one line call such as:
MiddleMan().b.assign(a.prop);
And at the same time I needed to optionally process additional stuff when the property is assigned, so a direct assignment like a.prop = MiddleMan().b;, for now I decided to utilize somewhat unusual syntax with overloading the & operator, which results in usage as:
a.prop = MiddleMan().b;
or optionally:
a.prop = MiddleMan().b & doAdditionalStuff;
Here's the implementation:
class B {
B operator &(Function doAdditionalStuff) {
if (doAdditionalStuff != null)
doAdditionalStuff();
return this;
}
}
To give it more sense of what I am trying to achieve, my lib is supposed to work in context with the Provider library. User code example:
class MyStore with ChangeNotifier {
B prop;
MyStore() {
// MiddleMan is Singleton
prop = MiddleMan().b;
// or alternatively
prop = MiddleMan().b & notifyListeners;
}
}
Library code example:
class B {
List<Function> _reactives = [];
B operator &(Function notifyListenersCallback) {
if (notifyListenersCallback != null)
this._reactives.add(notifyListenersCallback);
return this;
}
// called on each internally triggered change
notifyListeners() {
for (Function reactive in this._reactives) {
if (reactive != null)
reactive();
}
}
}

Haxe macro to set null to calling instance?

Is there a way of setting a null to calling instance as a result of some "macro-function-call"?
Like so:
class A {
// ...
macro function DestroyItself() {
// ...
}
}
var a:A = new A();
// ...
a.DestroyItself();
trace(a); // "null"
Yep:
macro public function destroy(self:Expr) {
return macro $self = null;
}
// ...
a.destroy();
In non-static macro functions first Expr argument is a ref to caller instance.
Once approach would be to create generic tools to null any instance.
package ;
class Tools
{
/**
* Simply assigns null to the instance
* See more at: http://code.haxe.org/category/macros/generating-code-in-a-macro.html
*
* #param instance - Any
* #return haxe.macro.Expr
*/
public static macro function nullMe(instance : haxe.macro.Expr.ExprOf<Dynamic>) : haxe.macro.Expr
{
return macro {
${instance} = null;
};
}
}
This use the using Tools; to generically null any instance, but I would not recommend this. I'd use the per-class approach.
Main.hx
package ;
class Main {
static function main() {
// Construct
var instance = new SomeClass();
// Destroy
instance.destroy();
// Trace null
trace(instance);
}
}
SomeClass.hx
package ;
class SomeClass
{
public function new()
{
trace("Hello from SomeClass!");
}
private function preDestroy()
{
trace("The end is nigh!");
}
public macro function destroy(self : haxe.macro.Expr) : haxe.macro.Expr
{
return macro {
#:privateAccess ${self}.preDestroy();
${self} = null;
};
}
}
Compiled JS
// Generated by Haxe 3.4.2
(function () { "use strict";
var Main = function() { };
Main.main = function() {
var instance = new SomeClass();
instance.preDestroy();
instance = null;
console.log(instance);
};
var SomeClass = function() {
console.log("Hello from SomeClass!");
};
SomeClass.prototype = {
preDestroy: function() {
console.log("The end is nigh!");
}
};
Main.main();
})();

TypeScript class decorator that modifies object instance

I'm making a plugin for Aurelia and need a class decorator that
adds attributes to the new object instance, and
calls an external function with the new object as an argument.
I've looked through examples, and so far I've put together ("pseudo-ish" code)
return function addAndCall(target: any): any {
var original = target;
var newConstructor = function (...args) {
original.apply(this, args);
this.newAttribute = "object instance value";
ExternalModule.externalFunction(this);
};
newConstructor.prototype = Object.create(original.prototype);
newConstructor.prototype.constructor = original;
return <any>newConstructor;
}
but
I'm not entirely clear on the details here (or what is actually needed), and
it might not work properly since I'm getting Aurelia errors when using objects instantiated from classes with this decorator (and I suspect it's my decorator rather than the Aurelia framework that's buggy).
Any help and explanation would be greatly appreciated!
Why not just assign those properties to the prototype, and subsequently assign to the instance on first invocation
// decorator
function addAndCall(cb: Function, newField: string) {
// cb is now available in the decorator
return function(ctor: Function): void {
Object.defineProperty(ctor.prototype, newField, {
value: function(...args: any[]) {
return Object.defineProperty(this, newField, {
value: function(...args: any[]) {
console.log(newField, ...args);
}
})[newField](...args);
}
});
cb(ctor);
}
}
let callMe = (decoratedCtor) => console.log(decoratedCtor);
#addAndCall(callMe, 'propertyName')
class AddToMe {}
let addToMe = new AddToMe();
(<any>addToMe).propertyName(1, 2);
Here's a working version:
function addAndCall(target: any) {
var original = target;
function construct(constructor, args) {
var c: any = function () {
this.newAttribute = "object instance value";
ExternalModule.externalFunction(this);
return constructor.apply(this, args);;
}
c.prototype = constructor.prototype;
return new c();
}
var f: any = function (...args) {
return construct(original, args);
}
f.prototype = original.prototype;
return f;
}
(code in playground)