| 
1 |  | -import {global} from 'angular2/src/facade/lang';  | 
 | 1 | +import {global, Type, isFunction, stringify} from 'angular2/src/facade/lang';  | 
2 | 2 | 
 
  | 
3 |  | -export function makeDecorator(annotationCls) {  | 
4 |  | -  return function(...args) {  | 
5 |  | -    var Reflect = global.Reflect;  | 
6 |  | -    if (!(Reflect && Reflect.getMetadata)) {  | 
7 |  | -      throw 'reflect-metadata shim is required when using class decorators';  | 
 | 3 | +export interface ClassDefinition {  | 
 | 4 | +  extends?: Type;  | 
 | 5 | +  constructor: (Function | Array<any>);  | 
 | 6 | +}  | 
 | 7 | + | 
 | 8 | +export interface TypeDecorator {  | 
 | 9 | +  (cls: any): any;  | 
 | 10 | +  annotations: Array<any>;  | 
 | 11 | +  Class(obj: ClassDefinition): Type;  | 
 | 12 | +}  | 
 | 13 | + | 
 | 14 | +function extractAnnotation(annotation: any) {  | 
 | 15 | +  if (isFunction(annotation) && annotation.hasOwnProperty('annotation')) {  | 
 | 16 | +    // it is a decorator, extract annotation  | 
 | 17 | +    annotation = annotation.annotation;  | 
 | 18 | +  }  | 
 | 19 | +  return annotation;  | 
 | 20 | +}  | 
 | 21 | + | 
 | 22 | +function applyParams(fnOrArray: (Function | Array<any>), key: string): Function {  | 
 | 23 | +  if (fnOrArray === Object || fnOrArray === String || fnOrArray === Function ||  | 
 | 24 | +      fnOrArray === Number || fnOrArray === Array) {  | 
 | 25 | +    throw new Error(`Can not use native ${stringify(fnOrArray)} as constructor`);  | 
 | 26 | +  }  | 
 | 27 | +  if (isFunction(fnOrArray)) {  | 
 | 28 | +    return <Function>fnOrArray;  | 
 | 29 | +  } else if (fnOrArray instanceof Array) {  | 
 | 30 | +    var annotations: Array<any> = fnOrArray;  | 
 | 31 | +    var fn: Function = fnOrArray[fnOrArray.length - 1];  | 
 | 32 | +    if (!isFunction(fn)) {  | 
 | 33 | +      throw new Error(  | 
 | 34 | +          `Last position of Class method array must be Function in key ${key} was '${stringify(fn)}'`);  | 
8 | 35 |     }  | 
9 |  | -    var annotationInstance = Object.create(annotationCls.prototype);  | 
10 |  | -    annotationCls.apply(annotationInstance, args);  | 
11 |  | -    return function(cls) {  | 
 | 36 | +    var annoLength = annotations.length - 1;  | 
 | 37 | +    if (annoLength != fn.length) {  | 
 | 38 | +      throw new Error(  | 
 | 39 | +          `Number of annotations (${annoLength}) does not match number of arguments (${fn.length}) in the function: ${stringify(fn)}`);  | 
 | 40 | +    }  | 
 | 41 | +    var paramsAnnotations: Array<Array<any>> = [];  | 
 | 42 | +    for (var i = 0, ii = annotations.length - 1; i < ii; i++) {  | 
 | 43 | +      var paramAnnotations: Array<any> = [];  | 
 | 44 | +      paramsAnnotations.push(paramAnnotations);  | 
 | 45 | +      var annotation = annotations[i];  | 
 | 46 | +      if (annotation instanceof Array) {  | 
 | 47 | +        for (var j = 0; j < annotation.length; j++) {  | 
 | 48 | +          paramAnnotations.push(extractAnnotation(annotation[j]));  | 
 | 49 | +        }  | 
 | 50 | +      } else if (isFunction(annotation)) {  | 
 | 51 | +        paramAnnotations.push(extractAnnotation(annotation));  | 
 | 52 | +      } else {  | 
 | 53 | +        paramAnnotations.push(annotation);  | 
 | 54 | +      }  | 
 | 55 | +    }  | 
 | 56 | +    Reflect.defineMetadata('parameters', paramsAnnotations, fn);  | 
 | 57 | +    return fn;  | 
 | 58 | +  } else {  | 
 | 59 | +    throw new Error(  | 
 | 60 | +        `Only Function or Array is supported in Class definition for key '${key}' is '${stringify(fnOrArray)}'`);  | 
 | 61 | +  }  | 
 | 62 | +}  | 
12 | 63 | 
 
  | 
13 |  | -      var annotations = Reflect.getMetadata('annotations', cls);  | 
14 |  | -      annotations = annotations || [];  | 
15 |  | -      annotations.push(annotationInstance);  | 
16 |  | -      Reflect.defineMetadata('annotations', annotations, cls);  | 
17 |  | -      return cls;  | 
 | 64 | +export function Class(clsDef: ClassDefinition): Type {  | 
 | 65 | +  var constructor = applyParams(  | 
 | 66 | +      clsDef.hasOwnProperty('constructor') ? clsDef.constructor : undefined, 'constructor');  | 
 | 67 | +  var proto = constructor.prototype;  | 
 | 68 | +  if (clsDef.hasOwnProperty('extends')) {  | 
 | 69 | +    if (isFunction(clsDef.extends)) {  | 
 | 70 | +      (<Function>constructor).prototype = proto =  | 
 | 71 | +          Object.create((<Function>clsDef.extends).prototype);  | 
 | 72 | +    } else {  | 
 | 73 | +      throw new Error(  | 
 | 74 | +          `Class definition 'extends' property must be a constructor function was: ${stringify(clsDef.extends)}`);  | 
 | 75 | +    }  | 
 | 76 | +  }  | 
 | 77 | +  for (var key in clsDef) {  | 
 | 78 | +    if (key != 'extends' && key != 'prototype' && clsDef.hasOwnProperty(key)) {  | 
 | 79 | +      proto[key] = applyParams(clsDef[key], key);  | 
18 | 80 |     }  | 
19 | 81 |   }  | 
 | 82 | +  return <Type>constructor;  | 
20 | 83 | }  | 
21 | 84 | 
 
  | 
22 |  | -export function makeParamDecorator(annotationCls): any {  | 
23 |  | -  return function(...args) {  | 
24 |  | -    var Reflect = global.Reflect;  | 
25 |  | -    if (!(Reflect && Reflect.getMetadata)) {  | 
26 |  | -      throw 'reflect-metadata shim is required when using parameter decorators';  | 
 | 85 | +var Reflect = global.Reflect;  | 
 | 86 | +if (!(Reflect && Reflect.getMetadata)) {  | 
 | 87 | +  throw 'reflect-metadata shim is required when using class decorators';  | 
 | 88 | +}  | 
 | 89 | + | 
 | 90 | +export function makeDecorator(annotationCls, chainFn: (fn: Function) => void = null): (...args) =>  | 
 | 91 | +    (cls: any) => any {  | 
 | 92 | +  function DecoratorFactory(objOrType): (cls: any) => any {  | 
 | 93 | +    var annotationInstance = new (<any>annotationCls)(objOrType);  | 
 | 94 | +    if (this instanceof annotationCls) {  | 
 | 95 | +      return annotationInstance;  | 
 | 96 | +    } else {  | 
 | 97 | +      var chainAnnotation = isFunction(this) && this.annotations instanceof Array ?  | 
 | 98 | +                                                                                this.annotations :  | 
 | 99 | +                                                                                [];  | 
 | 100 | +      chainAnnotation.push(annotationInstance);  | 
 | 101 | +      var TypeDecorator: TypeDecorator = <TypeDecorator>function TypeDecorator(cls) {  | 
 | 102 | +        var annotations = Reflect.getMetadata('annotations', cls);  | 
 | 103 | +        annotations = annotations || [];  | 
 | 104 | +        annotations.push(annotationInstance);  | 
 | 105 | +        Reflect.defineMetadata('annotations', annotations, cls);  | 
 | 106 | +        return cls;  | 
 | 107 | +      };  | 
 | 108 | +      TypeDecorator.annotations = chainAnnotation;  | 
 | 109 | +      TypeDecorator.Class = Class;  | 
 | 110 | +      if (chainFn) chainFn(TypeDecorator);  | 
 | 111 | +      return TypeDecorator;  | 
27 | 112 |     }  | 
 | 113 | +  }  | 
 | 114 | +  DecoratorFactory.prototype = Object.create(annotationCls.prototype);  | 
 | 115 | +  return DecoratorFactory;  | 
 | 116 | +}  | 
 | 117 | + | 
 | 118 | +export function makeParamDecorator(annotationCls): any {  | 
 | 119 | +  function ParamDecoratorFactory(...args) {  | 
28 | 120 |     var annotationInstance = Object.create(annotationCls.prototype);  | 
29 | 121 |     annotationCls.apply(annotationInstance, args);  | 
30 |  | -    return function(cls, unusedKey, index) {  | 
31 |  | -      var parameters: Array<Array<any>> = Reflect.getMetadata('parameters', cls);  | 
32 |  | -      parameters = parameters || [];  | 
33 |  | - | 
34 |  | -      // there might be gaps if some in between parameters do not have annotations.  | 
35 |  | -      // we pad with nulls.  | 
36 |  | -      while (parameters.length <= index) {  | 
37 |  | -        parameters.push(null);  | 
38 |  | -      }  | 
 | 122 | +    if (this instanceof annotationCls) {  | 
 | 123 | +      return annotationInstance;  | 
 | 124 | +    } else {  | 
 | 125 | +      function ParamDecorator(cls, unusedKey, index) {  | 
 | 126 | +        var parameters: Array<Array<any>> = Reflect.getMetadata('parameters', cls);  | 
 | 127 | +        parameters = parameters || [];  | 
39 | 128 | 
 
  | 
40 |  | -      parameters[index] = parameters[index] || [];  | 
41 |  | -      var annotationsForParam: Array<any> = parameters[index];  | 
42 |  | -      annotationsForParam.push(annotationInstance);  | 
 | 129 | +        // there might be gaps if some in between parameters do not have annotations.  | 
 | 130 | +        // we pad with nulls.  | 
 | 131 | +        while (parameters.length <= index) {  | 
 | 132 | +          parameters.push(null);  | 
 | 133 | +        }  | 
 | 134 | + | 
 | 135 | +        parameters[index] = parameters[index] || [];  | 
 | 136 | +        var annotationsForParam: Array<any> = parameters[index];  | 
 | 137 | +        annotationsForParam.push(annotationInstance);  | 
 | 138 | + | 
 | 139 | +        Reflect.defineMetadata('parameters', parameters, cls);  | 
 | 140 | +        return cls;  | 
 | 141 | +      }  | 
43 | 142 | 
 
  | 
44 |  | -      Reflect.defineMetadata('parameters', parameters, cls);  | 
45 |  | -      return cls;  | 
 | 143 | +      (<any>ParamDecorator).annotation = annotationInstance;  | 
 | 144 | +      return ParamDecorator;  | 
46 | 145 |     }  | 
47 | 146 |   }  | 
 | 147 | +  ParamDecoratorFactory.prototype = Object.create(annotationCls.prototype);  | 
 | 148 | +  return ParamDecoratorFactory;  | 
48 | 149 | }  | 
0 commit comments