2015-05-27 44 views
2

想我有这个类:按名称生成静态存取

abstract class DynamicallyAccessible{ 
    operator [](String key){ 
    throw 'DynamicallyAcessible [] operator is not implemented'; 
    } 

    operator []=(String key,Object value){ 
    throw 'DynamicallyAcessible []= operator is not implemented'; 
    } 

    call(String methodName,List<Object> params){ 
    throw 'DynamicallyAcessible call method is not implemented'; 
    } 
} 

和这个类延伸以上:

class Model extends DynamicallyAccessible{ 

    String _a; 
    String get a => _a; 
    set a(v)=> _a=v; 

    String _b; 
    String get b => _b; 
    set b(v)=> _b=v; 

    int c; 


    dummyMethod(int a,int b){ 
    return 6+a+b; 
    } 

    //this is to be generated by the transformer 
    //calls a getter or returns a value field by name 
    operator [](String key){ 
    switch (key){ 
     case 'a':return a; 
     case 'b':return b; 
     case 'c':return c; 
     default:throw 'no getter or field called $key'; 
    } 
    } 

    //calls a setter or sets a field by name 
    operator []=(String key,Object value){ 
    switch (key){ 
     case 'a':a=value;return; 
     case 'b':b=value;return; 
     case 'c':c=value;return; 
     default:throw 'no setter or field called $key'; 
    } 
    } 

    //calls a method by name 
    call(String key,List<Object> params){ 
    switch(key){ 
     case 'dummyMethod':return dummyMethod(params[0],params[1]); 
     default : throw 'no method called $key'; 
    } 
    } 
} 

我手动这是浪费时间,落实方法,我的问题是做一个变压器,做一些类似的存在或我必须从头开始写一个,如果没有任何建议,使这个更好?

的原因是为了避免使用镜子与dart2js

+0

所以,你要使用的类就像一张地图,也喜欢与自动完成或东西您的域普通类的,你不想使用反射? –

+0

@GünterZöchbauer准确地说,我知道如何为此编写一个变换器,但我想知道它是否已经完成,基本上它就像使用[]一样可以通过名称访问成员,就像javascript一样,因为镜像对于dar2js是无用的和痛苦的也减慢了速度 – FPGA

+0

我从来没有见过类似的东西,但我可以想象它是有用的。 –

回答

3

下面是使用该分析器包地发现延伸DynamicallyAccessible类和注入的代码对操作者[]的变压器的例子。

import 'package:barback/barback.dart'; 
import 'package:analyzer/analyzer.dart'; 

class DynamicallyAccessibleTransformer extends Transformer { 

    DynamicallyAccessibleTransformer.asPlugin(); 

    get allowedExtensions => '.dart'; 

    apply(Transform transform) async { 
    var content = await transform.readInputAsString(transform.primaryInput.id); 
    var newContent = _transformDartFile(content); 

    transform.addOutput(new Asset.fromString(transform.primaryInput.id, newContent)); 
    } 

    String _transformDartFile(String content) { 
    CompilationUnit unit = parseCompilationUnit(content); 

    String newContent = content; 

    for (ClassDeclaration declaration in unit.declarations.reversed.where((d) => d is ClassDeclaration)) { 
     if (_isDynamicallyAccessible(declaration)) { 
     String sourceToInject = _createSourceToInject(declaration); 

     String before = content.substring(0, declaration.endToken.offset); 
     String after = content.substring(declaration.endToken.offset); 

     newContent = before + "\n$sourceToInject\n" + after; 
     } 
    } 

    return newContent; 
    } 

    /// TODO: this is a fragile approach as we only check for the class name 
    /// and not the library from where it comes. We probably should resolve 
    /// the Element and check the library. 
    _isDynamicallyAccessible(ClassDeclaration declaration) { 
    ExtendsClause extendsClause = declaration.extendsClause; 
    if (extendsClause != null) { 
     return extendsClause.superclass.name.name == 'DynamicallyAccessible'; 
    } 
    return false; 
    } 

    _createSourceToInject(ClassDeclaration declaration) { 
    //TODO: do the same things for setters, methods and variable declaration. 
    String getterCases = declaration.members 
     .where((member) => member is MethodDeclaration && member.isGetter) 
     .map((MethodDeclaration getter) => getter.name.name) 
     .map((String name) => " case '$name': return $name;") 
     .join('\n'); 

    return ''' 
    operator [](String key) { 
    switch (key) { 
$getterCases 
     default:throw 'no getter called \$key'; 
    } 
    } 
'''; 
    } 
} 
1

这里是我的版本,还支持调用方法和位置命名的参数还没有深入的检验,但它工作正常

//TODO:support multiple inheritance where the sub class gets access to the super class members 
class DynamicTransformer extends Transformer { 


    DynamicTransformer.asPlugin(); 


    String get allowedExtensions => ".dart"; 


    Future apply(Transform transform) async{ 
    var logger = new BuildLogger(transform); 

     var content = await transform.primaryInput.readAsString(); 
     var sourceFile = new SourceFile(content); 
     var transaction = _transformCompilationUnit(content,sourceFile,logger); 
     var id = transform.primaryInput.id; 
     if(transaction.hasEdits){ 
     var printer = transaction.commit(); 
     var url = id.path.startsWith('lib/') 
        ? 'package:${id.package}/${id.path.substring(4)}' : id.path; 
     printer.build(url); 
     transform.addOutput(new Asset.fromString(id,printer.text)); 
     } 
     return logger.writeOutput(); 
    } 

    Future<bool> isPrimary(id) { 
    return new Future.value(id.extension == '.dart'); 
    } 


    TextEditTransaction _transformCompilationUnit(
    String inputCode, SourceFile sourceFile, BuildLogger logger) { 
    var unit = parseCompilationUnit(inputCode, suppressErrors: true); 
    var editor = new TextEditTransaction(inputCode, sourceFile); 

    unit.declarations 
    .where((dec) => dec is ClassDeclaration && 
    isSubClassOrHasMixinOf(dec, 'DynamicAccess')) 
    .forEach((classDec) { 
     _transformClass(classDec,editor,sourceFile,logger); 
    }); 

    return editor; 
    } 

    void _transformClass(ClassDeclaration cls, TextEditTransaction editor, 
         SourceFile file, BuildLogger logger) { 



    //TODO:throw another exception for private fields 

    var readWriteFieldNames = getClassDeclarationFieldDeclarationNames(cls,public:true); 
    var readOnlyFieldNames = getClassDeclarationFieldDeclarationNames(cls,public:true,readOnly:true); 
    var getters = getClassDeclarationMethodDeclarationNames(cls,getter:true).union(readWriteFieldNames).union(readOnlyFieldNames); 
    var setters = getClassDeclarationMethodDeclarationNames(cls,setter:true).union(readWriteFieldNames); 
    var methods = getClassDeclarationMethodDeclarations(cls,normal:true); 

    var methodsString = _buildSquareBracketsOperatorGet(getters); 
    methodsString+=_buildSquareBracketsOperatorAssignment(setters,readOnlyFieldNames); 
    methodsString+=_buildCallMethod(methods); 
    editor.edit(cls.endToken.offset,cls.endToken.offset,'$methodsString'); 

    } 

    //build [] operator method 
    String _buildSquareBracketsOperatorGet(Set<String> getters) { 
    var sb = new StringBuffer(); 
    sb.writeln('operator [](String key){'); 
    sb.writeln('switch (key){'); 
    getters.forEach((getter) { 
     sb.writeln("case '$getter': return this.$getter;"); 
    }); 
    sb.writeln("default:throw 'no getter or field called \$key';"); 
    sb.writeln('}}'); 
    return sb.toString(); 
    } 

    String _buildSquareBracketsOperatorAssignment(Set<String> setters,Set<String> readOnlyFields) { 
    var sb = new StringBuffer(); 
    sb.writeln('operator []=(String key,Object value){'); 
    sb.writeln('switch (key){'); 
    setters.forEach((setter) { 
     sb.writeln("case '$setter':this.$setter=value;return;"); 
    }); 
    readOnlyFields.forEach((readOnlyField) { 
     sb.writeln("case '$readOnlyField':throw 'field \$key is read only';return;"); 
    }); 
    sb.writeln("default:throw 'no setter or field called \$key';"); 
    sb.writeln('}}'); 
    return sb.toString(); 
    } 


    String _buildCallMethod(Set<MethodDeclaration> methods){ 
    var sb = new StringBuffer(); 
    sb.writeln('call(String key,{List<Object> required:null,List<Object> positional:null,Map<String,Object> named:null}){'); 
    sb.writeln('switch (key){'); 
    methods.forEach((md){ 
     sb.writeln("case '${md.name.name}': return this.${buildMethodCallString(md)}"); 
    }); 
    sb.writeln("default:throw 'no setter or field called \$key';"); 
    sb.writeln('}}'); 
    return sb.toString(); 
    } 
} 

一些实用方法:

isSubClassOrHasMixinOf(ClassDeclaration classDec, String superClassOrMixinName) { 
    if(classDec.withClause!=null){ 
    print(classDec.withClause.mixinTypes); 
    } 
    if ((classDec.extendsClause != null && 
    classDec.extendsClause.superclass.name.name == superClassOrMixinName) || 
    (classDec.withClause != null && 
    classDec.withClause.mixinTypes.any((type)=>type.name.name==superClassOrMixinName))) { 
    return true; 
    } 
    return false; 
} 

Set<String> getClassDeclarationFieldDeclarationNames(ClassDeclaration cd, 
    {public: true, private: false, readOnly: false}) { 
    var fieldNames = new Set<String>(); 

    cd.members.forEach((m) { 
    if (m is FieldDeclaration) { 
     var fd = m as FieldDeclaration; 
     if ((fd.fields.isFinal || fd.fields.isConst) && readOnly || (!readOnly && !fd.fields.isFinal && !fd.fields.isConst)) { 
     fd.fields.variables.forEach((variable) { 
      var fName = variable.name.name; 
      if ((Identifier.isPrivateName(fName) && private) || (!Identifier.isPrivateName(fName) && public)) { 
      fieldNames.add(fName); 
      } 
     }); 
     } 
    } 
    }); 
    return fieldNames; 
} 

Set<FieldDeclaration> getClassDeclarationFieldDeclarations(ClassDeclaration cd, 
    {public: true, private: false, readOnly: false}) { 
    var fieldsDeclarations = new Set<FieldDeclaration>(); 

    cd.members.forEach((m) { 
    if (m is FieldDeclaration) { 
     var fd = m as FieldDeclaration; 
     if ((fd.fields.isFinal || fd.fields.isConst) && readOnly || (!readOnly && !fd.fields.isFinal && !fd.fields.isConst)) { 
     fd.fields.variables.forEach((variable) { 
      if ((Identifier.isPrivateName(variable.name.name) && private) || (!Identifier.isPrivateName(variable.name.name) && public)) { 
      fieldsDeclarations.add(fd); 
      } 
     }); 
     } 
    } 
    }); 
    return fieldsDeclarations; 
} 


String buildMethodCallString(MethodDeclaration md){ 
    var sb = new StringBuffer(); 

    sb.write('${md.name.name}('); 

    var requiredParams = getMethodFormalParameterDeclarations(md,required:true); 
    if(requiredParams.length>0){ 
    for(int i=0;i<requiredParams.length;i++){ 
     sb.write('required[$i],'); 
    }; 
    } 

    var positionalParams = getMethodFormalParameterDeclarations(md,positional:true); 
    if(positionalParams.length>0){ 
    for(int i=0;i<positionalParams.length;i++){ 
     sb.write('(positional==null || positional[$i]==null)?${positionalParams[i].childEntities.last}:positional[$i],'); 
    }; 
    } 

    var namedParams = getMethodFormalParameterDeclarations(md,named:true); 
    if(namedParams.length > 0){ 
    for(int i=0;i<namedParams.length;i++){ 

     sb.write("${namedParams[i].identifier.name}:(named==null || !named.containsKey('${namedParams[i].identifier}'))?${namedParams[i].childEntities.last}:named['${namedParams[i].identifier}'],"); 
    }; 
    } 

    sb.write(');'); 
    return sb.toString().replaceAll(r',)',')'); 
} 

Set<MethodDeclaration> getClassDeclarationMethodDeclarations(
    ClassDeclaration cd, {public: true, private: false, getter: false, 
    setter: false, operator: false, normal: false}) { 
    var methodDeclarations = new Set<MethodDeclaration>(); 

    cd.members.forEach((d) { 
    if (d is MethodDeclaration) { 
     var md = d as MethodDeclaration; 
     var mName = md.name.name; 
     if ((Identifier.isPrivateName(mName) && private) || 
     (!Identifier.isPrivateName(mName) && public)) { 
     if (md.isSetter && setter) { 
      methodDeclarations.add(md); 
     } else if (md.isGetter && getter) { 
      methodDeclarations.add(md); 
     } else if (md.isOperator && operator) { 
      //to do warn if [] []= are already overloaded and terminate 
      return; 
     } else if (normal && !md.isOperator && !md.isGetter && !md.isSetter) { 
      methodDeclarations.add(md); 
     } 
     } 
    } 
    }); 

    return methodDeclarations; 
} 

Set<String> getClassDeclarationMethodDeclarationNames(
    ClassDeclaration cd, {public: true, private: false, getter: false, 
    setter: false, operator: false, normal: false}) { 

    var methodDeclarationNames = new Set<String>(); 

    cd.members.forEach((d) { 
    if (d is MethodDeclaration) { 
     var md = d as MethodDeclaration; 
     var mName = md.name.name; 
     if ((Identifier.isPrivateName(mName) && private) || 
     (!Identifier.isPrivateName(mName) && public)) { 
     if (md.isSetter && setter) { 
      methodDeclarationNames.add(mName); 
     } else if (md.isGetter && getter) { 
      methodDeclarationNames.add(mName); 
     } else if (md.isOperator && operator) { 
      //to do warn if [] []= are already overloaded and terminate 
      return; 
     } else if (normal && !md.isOperator && !md.isGetter && !md.isSetter) { 
      methodDeclarationNames.add(mName); 
     } 
     } 
    } 
    }); 

    return methodDeclarationNames; 

} 

List<FormalParameter> getMethodFormalParameterDeclarations(MethodDeclaration md, 
                  {required: false, positional: false, named: false}) { 
    var formalParameters = new List<FormalParameter>(); 
    md.parameters.parameters.forEach((fp) { 
    if ((fp.kind == ParameterKind.REQUIRED && required) || 
    (fp.kind == ParameterKind.POSITIONAL && positional) || 
    (fp.kind == ParameterKind.NAMED && named)) { 
     formalParameters.add(fp); 
    } 
    }); 
    return formalParameters; 
} 

例如:

class Model extends Object with DynamicAccess{ 
    String _a; 
    String get a => _a; 
    set a(v)=> _a=v; 

    String _b; 
    String get b => _b; 
    set b(v)=> _b=v; 

    final int c=9; 

    String _z = 'xx'; 


    dummyMethod(int a,int b){ 
    return 6+a+b; 
    } 

    dummyNoParams(){ 
    return 0; 
    } 

    dummyMethodWithPositionalParams(int a,String d,[int x=4,y=6]){ 
    return '${a+x+y} $d'; 
    } 


    dummyMethodWithNamedParams(int x,int y,{int f:4,String g:'no'}){ 
    return '${x+y+f} $g'; 
    } 
} 

给出:

class Model extends Object with DynamicAccess { 
    String _a; 
    String get a => _a; 
    set a(v) => _a = v; 

    String _b; 
    String get b => _b; 
    set b(v) => _b = v; 

    final int c = 9; 

    String _z = 'xx'; 

    dummyMethod(int a, int b) { 
    return 6 + a + b; 
    } 

    dummyNoParams() { 
    return 0; 
    } 

    dummyMethodWithPositionalParams(int a, String d, [int x = 4, y = 6]) { 
    return '${a+x+y} $d'; 
    } 

    dummyMethodWithNamedParams(int x, int y, {int f: 4, String g: 'no'}) { 
    return '${x+y+f} $g'; 
    } 
    operator [](String key) { 
    switch (key) { 
     case 'a': 
     return this.a; 
     case 'b': 
     return this.b; 
     case 'c': 
     return this.c; 
     default: 
     throw 'no getter or field called $key'; 
    } 
    } 
    operator []=(String key, Object value) { 
    switch (key) { 
     case 'a': 
     this.a = value; 
     return; 
     case 'b': 
     this.b = value; 
     return; 
     case 'c': 
     throw 'field $key is read only'; 
     return; 
     default: 
     throw 'no setter or field called $key'; 
    } 
    } 
    call(String key, {List<Object> required: null, List<Object> positional: null, 
     Map<String, Object> named: null}) { 
    switch (key) { 
     case 'dummyMethod': 
     return this.dummyMethod(required[0], required[1]); 
     case 'dummyNoParams': 
     return this.dummyNoParams(); 
     case 'dummyMethodWithPositionalParams': 
     return this.dummyMethodWithPositionalParams(required[0], required[1], 
      (positional == null || positional[0] == null) ? 4 : positional[0], 
      (positional == null || positional[1] == null) ? 6 : positional[1]); 
     case 'dummyMethodWithNamedParams': 
     return this.dummyMethodWithNamedParams(required[0], required[1], 
      f: (named == null || !named.containsKey('f')) ? 4 : named['f'], 
      g: (named == null || !named.containsKey('g')) ? 'no' : named['g']); 
     default: 
     throw 'no setter or field called $key'; 
    } 
    } 
}