import { PropertyConverter , FlushPriority , FetchType } from "../mappingModel" ; import { CollectionOptions } from "../collectionOptions" ; import { IndexOptions } from "../indexOptions" ; import { ChangeTrackingType } from "../mappingModel" ; import { CascadeFlags } from "../mappingModel" ; import { Constructor , ParameterlessConstructor } from "../../index" ; import { MappingModel } from "../mappingModel" ; import { MappingBuilderContext } from "./mappingBuilderContext" ; import { MappingBuilder } from "./mappingBuilder" ; import { Type , Property , Method } from "reflect-helper" ; import { EntityMappingBuilder } from "./entityMappingBuilder" ; import { ClassMappingBuilder } from "./classMappingBuilder" ; import { Index } from "../index" ; import { IdentityGenerator } from "../../config/configuration" ; hidden export enum AnnotationPriority { High = 100 , Medium = 50 , Low = 0 } hidden export class Annotation { } hidden export interface ClassAnnotation { /**
     * Indicates if the annotation should be inherited from a mapped superclass. Default is false.
     */ inherited ? : boolean ; processClassAnnotation ( context , mapping , annotation ) : void ; } hidden export interface PropertyAnnotation { processPropertyAnnotation ( context , mapping , property , symbol , annotation ) : void ; } hidden export interface MethodAnnotation { processMethodAnnotation ( context , mapping , method , annotation ) : void ; } hidden export interface MappingBuilderAnnotation { createBuilder ( context , type ) : MappingBuilder ; } hidden export interface TargetClassAnnotation { target : Constructor < any > | string ; } hidden export class EntityAnnotation extends Annotation implements MappingBuilderAnnotation { createBuilder ( context , type ) { var parentMapping = < MappingModel . EntityMapping > getParentMapping ( context , type ) ; if ( parentMapping && ( parentMapping . flags & MappingModel . MappingFlags . Entity ) == 0 ) { context . addError ( "Parent of mapping for '" + type . name + "' must be an entity mapping." ) ; return ; } return new EntityMappingBuilder ( context , type , MappingModel . createEntityMapping ( ( parentMapping ) ) ) ; } toString ( ) { return "@Entity" ; } } hidden export class EmbeddableAnnotation extends Annotation implements MappingBuilderAnnotation { createBuilder ( context , type ) { var parentMapping = getParentMapping ( context , type ) ; if ( parentMapping && ( parentMapping . flags & MappingModel . MappingFlags . Embeddable ) == 0 ) { context . addError ( "Parent of mapping for '" + type . name + "' must be an embeddable mapping." ) ; return ; } return new ClassMappingBuilder ( context , type , MappingModel . createClassMapping ( parentMapping ) ) ; } toString ( ) { return "@Embeddable" ; } } hidden export class ConverterAnnotation extends Annotation implements MappingBuilderAnnotation { converter : PropertyConverter ; converterCtr : ParameterlessConstructor < PropertyConverter > ; converterName : string ; param converter constructor ( converter ) { super ( ) ; if ( typeof converter === "string" ) { this . converterName = converter ; } else if ( typeof converter === "function" ) { this . converterCtr = < ParameterlessConstructor < PropertyConverter > > converter ; } else { this . converter = < PropertyConverter > converter ; } } toString ( ) { return "@Converter" ; } createBuilder ( context , type ) { return new MappingBuilder ( context , type , this . createMapping ( context ) ) ; } createMapping ( context ) { if ( this . converter ) { return MappingModel . createConverterMapping ( this . converter ) ; } if ( this . converterCtr ) { return MappingModel . createConverterMapping ( new this . converterCtr ( ) ) ; } if ( ! this . converterName ) { context . addError ( "A convert instance, constructor, or name must be specified." ) ; return ; } var converter = context . config . propertyConverters && context . config . propertyConverters [ this . converterName ] ; if ( ! converter ) { context . addError ( "Unknown converter '" + this . converterName + "'. Make sure to add your converter to propertyConverters in the configuration." ) ; return ; } return MappingModel . createConverterMapping ( converter ) ; } } hidden export class CollectionAnnotation extends Annotation implements ClassAnnotation { /**
     * The name of the collection to use.
     */ name : string ; /**
     * The order in which collections are flushed to the database. Higher priority collections are flushed first.
     */ flushPriority : FlushPriority ; /**
     * The name of the database to use.
     */ db : string ; /**
     * Collection options to pass to driver.
     */ options : CollectionOptions ; constructor ( name ) ; constructor ( description ? ) ; constructor ( nameOrDescription ? ) { super ( ) ; if ( typeof nameOrDescription === "string" ) { this . name = nameOrDescription ; } if ( typeof nameOrDescription === "object" ) { this . name = nameOrDescription . name ; this . flushPriority = nameOrDescription . flushPriority ; this . db = nameOrDescription . db ; this . options = nameOrDescription . options ; } } toString ( ) { return "@Collection" ; } processClassAnnotation ( context , mapping , annotation ) { if ( context . assertRootEntityMapping ( mapping ) ) { if ( annotation . name ) { mapping . collectionName = annotation . name ; } mapping . collectionOptions = annotation . options ; if ( annotation . db ) { mapping . databaseName = annotation . db ; } if ( annotation . flushPriority != null ) { mapping . flushPriority = annotation . flushPriority ; } } } } export interface CollectionDescription { flushPriority ? : FlushPriority ; name ? : string ; db ? : string ; options ? : CollectionOptions ; } hidden export class IndexAnnotation extends Annotation implements ClassAnnotation , PropertyAnnotation { /**
     * The index keys as an array of tuples [name, 1|-1] if the annotation is specified on a class.
     */ keys : [ string , number | string ] [ ] ; /**
     * The order of the index if annotation is specified on a property.
     */ order : number ; /**
     * Index options to pass to the driver.
     */ options : IndexOptions ; /**
     * Indicates annotation is inherited from a mapped superclass.
     */ inherited = true ; constructor ( args ) ; constructor ( args ? ) ; constructor ( args ? ) { super ( ) ; if ( args ) { this . keys = args . keys ; this . order = args . order ; this . options = args . options ; } } toString ( ) { return "@Index" ; } processClassAnnotation ( context , mapping , annotation ) { if ( context . assertEntityMapping ( mapping ) ) { this . _addIndex ( context , mapping , annotation ) ; } } processPropertyAnnotation ( context , mapping , property , symbol , annotation ) { if ( context . assertEntityMapping ( mapping ) ) { this . _addIndex ( context , mapping , { keys : [ [ property . name , annotation . order || 1 ] ] , options : annotation . options } ) ; } } private _addIndex ( context , mapping , value ) { if ( context . assertEntityMapping ( mapping ) ) { if ( ! Array . isArray ( value . keys ) || value . keys . length == 0 ) { context . addError ( "Missing or invalid property 'keys'." ) ; return ; } for ( let i = 0 ; i < value . keys . length ; i ++ ) { let key = value . keys [ i ] ; if ( ! Array . isArray ( key ) || key . length != 2 || typeof key [ 0 ] !== "string" ) { context . addError ( `Index key ${ i } is invalid. Key must be a tuple [path, order].` ) ; return ; } let order = value . keys [ i ] [ 1 ] ; if ( order != 1 && order != - 1 && order != 'text' ) { context . addError ( "Valid values for index order are 1, -1, or 'text'." ) ; return ; } } mapping . addIndex ( value ) ; } } } export interface ClassIndexDescription { keys : [ string , number | string ] [ ] ; options ? : IndexOptions ; } export interface PropertyIndexDescription { order ? : number | string ; options ? : IndexOptions ; } hidden export class VersionFieldAnnotation extends Annotation implements ClassAnnotation { param name constructor ( public name ) { super ( ) ; } toString ( ) { return "@VersionField" ; } processClassAnnotation ( context , mapping , annotation ) { if ( context . assertRootEntityMapping ( mapping ) ) { mapping . versionField = annotation . name ; mapping . versioned = true ; } } } hidden export class VersionedAnnotation extends Annotation implements ClassAnnotation { param enabled constructor ( public enabled = true ) { super ( ) ; } toString ( ) { return "@Versioned" ; } processClassAnnotation ( context , mapping , annotation ) { if ( context . assertRootEntityMapping ( mapping ) ) { mapping . versioned = annotation . enabled ; } } } hidden export class ChangeTrackingAnnotation extends Annotation implements ClassAnnotation { constructor ( public type ) { super ( ) ; } toString ( ) { return "@ChangeTracking" ; } processClassAnnotation ( context , mapping , annotation ) { if ( context . assertRootEntityMapping ( mapping ) ) { if ( ( mapping . flags & MappingModel . MappingFlags . Immutable ) != 0 ) { context . addError ( "Change tracking cannot be set on immutable entity." ) ; return ; } mapping . changeTracking = annotation . type ; } } } hidden export class IdentityAnnotation extends Annotation implements ClassAnnotation { identity : IdentityGenerator ; identityCtr : ParameterlessConstructor < IdentityGenerator > ; param identity constructor ( identity ) { super ( ) ; if ( typeof identity === "function" ) { this . identityCtr = < ParameterlessConstructor < IdentityGenerator > > identity ; } else { this . identity = < IdentityGenerator > identity ; } } toString ( ) { return "@Identity" ; } processClassAnnotation ( context , mapping , annotation ) { if ( context . assertRootEntityMapping ( mapping ) ) { var identity ; if ( this . identity ) { identity = this . identity ; } else if ( this . identityCtr ) { identity = new this . identityCtr ( ) ; } if ( ! identity ) { context . addError ( "An identity generator instance or constructor must be specified." ) ; return ; } mapping . identity = identity ; } } } hidden export class DiscriminatorFieldAnnotation extends Annotation implements ClassAnnotation { constructor ( public name ) { super ( ) ; } toString ( ) { return "@DiscriminatorField" ; } processClassAnnotation ( context , mapping , annotation ) { if ( context . assertRootClassMapping ( mapping ) ) { if ( ! annotation . name ) { context . addError ( "Missing discriminator field name." ) ; return ; } mapping . discriminatorField = annotation . name ; } } } hidden export class ImmutableAnnotation extends Annotation implements ClassAnnotation { toString ( ) { return "@Immutable" ; } processClassAnnotation ( context , mapping , annotation ) { if ( context . assertClassMapping ( mapping ) ) { mapping . flags |= MappingModel . MappingFlags . Immutable ; if ( ( mapping . flags & MappingModel . MappingFlags . Entity ) != 0 && ( mapping . flags & MappingModel . MappingFlags . InheritanceRoot ) != 0 ) { var entityMapping = ( < MappingModel . EntityMapping > mapping ) ; if ( entityMapping . changeTracking != null ) { context . addError ( "Change tracking cannot be set on immutable entity." ) ; return ; } entityMapping . changeTracking = ChangeTrackingType . None ; entityMapping . versioned = false ; } } } } hidden export class DiscriminatorValueAnnotation extends Annotation implements ClassAnnotation { constructor ( public value ) { super ( ) ; } toString ( ) { return "@DiscriminatorValue" ; } processClassAnnotation ( context , mapping , annotation ) { if ( context . assertClassMapping ( mapping ) ) { if ( ! annotation . value ) { context . addError ( "Missing discriminator value." ) ; return ; } try { mapping . setDiscriminatorValue ( annotation . value ) ; } catch ( err ) { context . addError ( err . message ) ; } } } } hidden export class InverseOfAnnotation extends Annotation implements PropertyAnnotation { constructor ( public propertyName ) { super ( ) ; } toString ( ) { return "@InverseOf" ; } processPropertyAnnotation ( context , mapping , property , symbol , annotation ) { property . inverseOf = annotation . propertyName ; property . setFlags ( MappingModel . PropertyFlags . InverseSide ) ; } } hidden export class CascadeAnnotation extends Annotation implements PropertyAnnotation { constructor ( public flags ) { super ( ) ; } toString ( ) { return "@Cascade" ; } processPropertyAnnotation ( context , mapping , property , symbol , annotation ) { property . setFlags ( annotation . flags & MappingModel . PropertyFlags . CascadeAll ) ; } } hidden export class FetchAnnotation extends Annotation implements PropertyAnnotation { constructor ( public type ) { super ( ) ; } toString ( ) { return "@Fetch" ; } processPropertyAnnotation ( context , mapping , property , symbol , annotation ) { property . setFlags ( annotation . type & ( MappingModel . PropertyFlags . FetchEager | MappingModel . PropertyFlags . FetchLazy ) ) ; } } hidden export class TypeAnnotation extends Annotation implements TargetClassAnnotation { constructor ( public target ) { super ( ) ; } toString ( ) { return "@Type" ; } } hidden export class ElementTypeAnnotation extends Annotation implements TargetClassAnnotation { constructor ( public target ) { super ( ) ; } toString ( ) { return "@ElementType" ; } } hidden export class MapKeyAnnotation extends Annotation { constructor ( public propertyName ) { super ( ) ; } toString ( ) { return "@MapKey" ; } } hidden export class FieldAnnotation extends Annotation implements PropertyAnnotation { name : string ; nullable : boolean ; readable : boolean ; constructor ( name ? ) ; constructor ( args ) ; constructor ( args ? ) { super ( ) ; if ( args ) { if ( typeof args === "string" ) { this . name = args ; } else { this . name = args . name ; this . nullable = args . nullable ; this . readable = args . readable ; } } } toString ( ) { return "@Field" ; } processPropertyAnnotation ( context , mapping , property , symbol , annotation ) { if ( annotation . name ) { property . field = annotation . name ; } if ( annotation . nullable !== undefined ) { property . nullable = annotation . nullable ; } if ( annotation . readable === false ) { property . setFlags ( MappingModel . PropertyFlags . WriteOnly ) ; } } } hidden export class ParentAnnotation extends Annotation implements PropertyAnnotation { toString ( ) { return "@Parent" ; } processPropertyAnnotation ( context , mapping , property , symbol , annotation ) { if ( context . assertEmbeddableMapping ( mapping ) ) { property . setFlags ( MappingModel . PropertyFlags . Parent | MappingModel . PropertyFlags . Ignored ) ; } } } export interface FieldDescription { /**
     * The name of the database document field.
     */ name ? : string ; /**
     * Indicates if null values are supported. If false then null values are not written to the database. Default is false.
     */ nullable ? : boolean ; /**
     * Indicates if the database document field should be read. Default is true. If set the `false` the database document field may still
     * be written to but the field value is not read when deserializing the document from the database. This is useful if your entity has
     * an accessor with a value calculated from other fields in the class that you want to store to the database for queries.
     */ readable ? : boolean ; } hidden export class EnumeratedAnnotation { constructor ( public members ) { } toString ( ) { return "@Enumerated" ; } } hidden function getParentMapping ( context , type ) { var baseType = type . baseType ; if ( baseType ) { var builder = context . getBuilder ( baseType ) ; if ( builder ) { return < MappingModel . ClassMapping > builder . mapping ; } } } hidden export class TransientAnnotation implements PropertyAnnotation { toString ( ) { return "@Transient" ; } processPropertyAnnotation ( context , mapping , property , symbol , annotation ) { property . field = null ; property . setFlags ( MappingModel . PropertyFlags . Ignored ) ; } }