1 /**
2  * HibernateD - Object-Relation Mapping for D programming language, with interface similar to Hibernate. 
3  * 
4  * Hibernate documentation can be found here:
5  * $(LINK http://hibernate.org/docs)$(BR)
6  * 
7  * Source file hibernated/metadata.d.
8  *
9  * This module contains implementation of Annotations parsing and ORM model metadata holder classes.
10  * 
11  * Copyright: Copyright 2013
12  * License:   $(LINK www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
13  * Author:   Vadim Lopatin
14  */
15 module hibernated.metadata;
16 
17 import std.ascii;
18 import std.conv;
19 import std.datetime;
20 import std.exception;
21 import std.stdio;
22 import std.string;
23 import std.traits;
24 import std.typecons;
25 import std.typetuple;
26 import std.variant;
27 import std.uuid;
28 
29 import ddbc.core;
30 import ddbc.common;
31 
32 import hibernated.annotations;
33 import hibernated.core;
34 import hibernated.type;
35 import hibernated.session;
36 import hibernated.dialect;
37 import hibernated.dialects.mysqldialect;
38 
39 // For backwards compatibily
40 // 'enforceEx' will be removed with 2.089
41 static if(__VERSION__ < 2080) {
42     alias enforceHelper = enforceEx;
43 } else {
44     alias enforceHelper = enforce;
45 }
46 
47 abstract class EntityMetaData {
48 
49     @property size_t length();
50     const(EntityInfo) opIndex(int index) const;
51     const(EntityInfo) opIndex(string entityName) const;
52     const(PropertyInfo) opIndex(string entityName, string propertyName) const;
53 
54     public string getEntityName(TypeInfo_Class type) const {
55         return getClassMap()[type].name;
56     }
57     
58     public string getEntityNameForClass(T)() const {
59         return getClassMap()[T.classinfo].name;
60     }
61 
62     int opApply(int delegate(ref const EntityInfo) dg) const;
63     
64 
65     public const(EntityInfo[]) getEntities() const;
66     public const(EntityInfo[string]) getEntityMap() const;
67     public const(EntityInfo[TypeInfo_Class]) getClassMap() const;
68     public const(EntityInfo) findEntity(string entityName) const;
69     public const(EntityInfo) findEntity(TypeInfo_Class entityClass) const;
70     public const(EntityInfo) findEntityForObject(Object obj) const;
71     public const(EntityInfo) getEntity(int entityIndex) const;
72     public int getEntityCount() const;
73     /// Entity factory
74     public Object createEntity(string entityName) const;
75     /// Fills all properties of entity instance from dataset
76     public int readAllColumns(Object obj, DataSetReader r, int startColumn) const;
77     /// Puts all properties of entity instance to dataset
78     public int writeAllColumns(Object obj, DataSetWriter w, int startColumn, bool exceptKey = false) const;
79 
80     public string generateFindAllForEntity(Dialect dialect, string entityName) const;
81 
82     public int getFieldCount(const EntityInfo ei, bool exceptKey) const;
83 
84     public string getAllFieldList(Dialect dialect, const EntityInfo ei, bool exceptKey = false) const;
85     public string getAllFieldList(Dialect dialect, string entityName, bool exceptKey = false) const;
86 
87     public string generateFindByPkForEntity(Dialect dialect, const EntityInfo ei) const;
88     public string generateFindByPkForEntity(Dialect dialect, string entityName) const;
89 
90     public string generateInsertAllFieldsForEntity(Dialect dialect, const EntityInfo ei) const;
91     public string generateInsertAllFieldsForEntity(Dialect dialect, string entityName) const;
92     public string generateInsertNoKeyForEntity(Dialect dialect, const EntityInfo ei) const;
93     public string generateUpdateForEntity(Dialect dialect, const EntityInfo ei) const;
94 
95     public Variant getPropertyValue(Object obj, string propertyName) const;
96     public void setPropertyValue(Object obj, string propertyName, Variant value) const;
97 }
98 
99 enum RelationType {
100     None,
101     Embedded,
102     OneToOne,
103     OneToMany,
104     ManyToOne,
105     ManyToMany,
106 }
107 
108 /// Metadata of entity property
109 class PropertyInfo {
110 public:
111     /// reads simple property value from data set to object
112     alias ReaderFunc = void function(Object, DataSetReader, int index);
113     /// writes simple property value to data set from object
114     alias WriterFunc = void function(Object, DataSetWriter, int index);
115     /// copy property from second passed object to first
116     alias CopyFunc = void function(Object, Object);
117     /// returns simple property as Variant
118     alias GetVariantFunc = Variant function(Object);
119     /// sets simple property from Variant
120     alias SetVariantFunc = void function(Object, Variant value);
121     /// returns true if property value of object is not null
122     alias IsNullFunc = bool function(Object);
123     /// returns true if key property of object is set (similar to IsNullFunc but returns true if non-nullable number is 0.
124     alias KeyIsSetFunc = bool function(Object);
125     /// returns OneToOne, ManyToOne or Embedded property as Object
126     alias GetObjectFunc = Object function(Object);
127     /// sets OneToOne, ManyToOne or Embedded property as Object
128     alias SetObjectFunc = void function(Object, Object);
129     /// sets lazy loader delegate for OneToOne, or ManyToOne property if it's Lazy! template instance
130     alias SetObjectDelegateFunc = void function(Object, Object delegate());
131     /// sets lazy loader delegate for OneToMany, or ManyToMany property if it's LazyCollection! template instance
132     alias SetCollectionDelegateFunc = void function(Object, Object[] delegate());
133     /// returns OneToMany or ManyToMany property value as object array
134     alias GetCollectionFunc = Object[] function(Object);
135     /// sets OneToMany or ManyToMany property value from object array
136     alias SetCollectionFunc = void function(Object, Object[]);
137     /// returns true if Lazy! or LazyCollection! property is loaded (no loader delegate set).
138     alias IsLoadedFunc = bool function(Object);
139     /// returns new generated primary key for property
140     alias GeneratorFunc = Variant function(Connection conn, const PropertyInfo prop);
141 
142     package EntityInfo _entity;
143     @property const(EntityInfo) entity() const { return _entity; }
144     @property const(EntityMetaData) metadata() const { return _entity._metadata; }
145 
146     immutable string propertyName;
147     immutable string columnName;
148     immutable Type columnType;
149     immutable int length;
150     immutable bool key;
151     immutable bool generated;
152     immutable bool nullable;
153     immutable string uniqueIndex;
154     immutable RelationType relation;
155     immutable bool lazyLoad;
156     immutable bool collection;
157 
158     immutable string referencedEntityName; // for @Embedded, @OneToOne, @OneToMany, @ManyToOne, @ManyToMany holds name of entity
159     package EntityInfo _referencedEntity; // for @Embedded, @OneToOne, @OneToMany, @ManyToOne, @ManyToMany holds entity info reference, filled in runtime
160     @property const(EntityInfo) referencedEntity() const { return _referencedEntity; }
161 
162     immutable string referencedPropertyName; // for @OneToOne, @OneToMany, @ManyToOne
163     package PropertyInfo _referencedProperty;
164     @property const(PropertyInfo) referencedProperty() const { return _referencedProperty; }
165 
166     package int _columnOffset; // offset from first column of this entity in selects
167     @property int columnOffset() const { return _columnOffset; } // offset from first column of this entity in selects
168 
169     package JoinTableInfo _joinTable;
170     @property const (JoinTableInfo) joinTable() const { return _joinTable; }
171 
172     immutable ReaderFunc readFunc;
173     immutable WriterFunc writeFunc;
174     immutable GetVariantFunc getFunc;
175     immutable SetVariantFunc setFunc;
176     immutable KeyIsSetFunc keyIsSetFunc;
177     immutable IsNullFunc isNullFunc;
178     immutable GetObjectFunc getObjectFunc;
179     immutable SetObjectFunc setObjectFunc;
180     immutable CopyFunc copyFieldFunc;
181     immutable GetCollectionFunc getCollectionFunc;
182     immutable SetCollectionFunc setCollectionFunc;
183     immutable SetObjectDelegateFunc setObjectDelegateFunc;
184     immutable SetCollectionDelegateFunc setCollectionDelegateFunc;
185     immutable IsLoadedFunc isLoadedFunc;
186     immutable GeneratorFunc generatorFunc;
187 
188     @property bool simple() const { return relation == RelationType.None; };
189     @property bool embedded() const { return relation == RelationType.Embedded; };
190     @property bool oneToOne() const { return relation == RelationType.OneToOne; };
191     @property bool oneToMany() const { return relation == RelationType.OneToMany; };
192     @property bool manyToOne() const { return relation == RelationType.ManyToOne; };
193     @property bool manyToMany() const { return relation == RelationType.ManyToMany; };
194 
195     this(string propertyName, string columnName, Type columnType, int length, bool key, bool generated, bool nullable, string uniqueIndex, RelationType relation, string referencedEntityName, string referencedPropertyName, ReaderFunc reader, WriterFunc writer, GetVariantFunc getFunc, SetVariantFunc setFunc, KeyIsSetFunc keyIsSetFunc, IsNullFunc isNullFunc, 
196             CopyFunc copyFieldFunc, 
197             GeneratorFunc generatorFunc = null,
198             GetObjectFunc getObjectFunc = null, 
199             SetObjectFunc setObjectFunc = null, 
200             GetCollectionFunc getCollectionFunc = null, 
201             SetCollectionFunc setCollectionFunc = null,
202             SetObjectDelegateFunc setObjectDelegateFunc = null, 
203             SetCollectionDelegateFunc setCollectionDelegateFunc = null, 
204             IsLoadedFunc isLoadedFunc = null,
205             bool lazyLoad = false, bool collection = false,
206             JoinTableInfo joinTable = null) {
207         this.propertyName = propertyName;
208         this.columnName = columnName;
209         this.columnType = cast(immutable Type)columnType;
210         this.length = length;
211         this.key = key;
212         this.generated = generated;
213         this.nullable = nullable;
214         this.relation = relation;
215         this.referencedEntityName =referencedEntityName;
216         this.referencedPropertyName = referencedPropertyName;
217         this.readFunc = reader;
218         this.writeFunc = writer;
219         this.getFunc = getFunc;
220         this.setFunc = setFunc;
221         this.keyIsSetFunc = keyIsSetFunc;
222         this.isNullFunc = isNullFunc;
223         this.getObjectFunc = getObjectFunc;
224         this.setObjectFunc = setObjectFunc;
225         this.copyFieldFunc = copyFieldFunc;
226         this.generatorFunc = generatorFunc;
227         this.lazyLoad = lazyLoad;
228         this.collection = collection;
229         this.setObjectDelegateFunc = setObjectDelegateFunc;
230         this.setCollectionDelegateFunc = setCollectionDelegateFunc;
231         this.getCollectionFunc = getCollectionFunc;
232         this.setCollectionFunc = setCollectionFunc;
233         this.isLoadedFunc = isLoadedFunc;
234         this._joinTable = joinTable;
235         this.uniqueIndex = uniqueIndex;
236     }
237 
238     package void updateJoinTable() {
239         assert(relation == RelationType.ManyToMany);
240         assert(_joinTable !is null);
241         _joinTable.setEntities(entity, referencedEntity);
242     }
243 
244     hash_t opHash() const {
245         return (cast(hash_t)(cast(void*)this)) * 31;
246     }
247 
248     bool opEquals(ref const PropertyInfo s) const {
249         return this == s;
250     }
251 
252     int opCmp(ref const PropertyInfo s) const {
253         return this == s ? 0 : (opHash() > s.opHash() ? 1 : -1);
254     }
255 
256     Variant[] getCollectionIds(Object obj) const {
257         assert(oneToMany || manyToMany);
258         Variant[] res;
259         Object[] list = getCollectionFunc(obj);
260         if (list is null)
261             return res;
262         foreach(item; list) {
263             res ~= referencedEntity.getKey(item);
264         }
265         return res;
266     }
267 }
268 
269 /// Metadata of single entity
270 class EntityInfo {
271 
272     package EntityMetaData _metadata;
273     @property const(EntityMetaData) metadata() const { return _metadata; }
274 
275     immutable string name;
276     immutable string tableName;
277     private PropertyInfo[] _properties;
278     @property const(PropertyInfo[]) properties() const { return _properties; }
279     package PropertyInfo [string] _propertyMap;
280     immutable TypeInfo_Class classInfo;
281     private int _keyIndex;
282     @property int keyIndex() const { return _keyIndex; }
283     private PropertyInfo _keyProperty;
284     @property const(PropertyInfo) keyProperty() const { return _keyProperty; }
285 
286     immutable bool embeddable;
287 
288 
289     int opApply(int delegate(ref const PropertyInfo) dg) const { 
290         int result = 0; 
291         for (int i = 0; i < _properties.length; i++) { 
292             result = dg(_properties[i]); 
293             if (result) break; 
294         } 
295         return result; 
296     }
297 
298     public this(string name, string tableName, bool embeddable, PropertyInfo [] properties, TypeInfo_Class classInfo) {
299         this.name = name;
300         this.tableName = tableName;
301         this.embeddable = embeddable;
302         this._properties = properties;
303         this.classInfo = cast(immutable TypeInfo_Class)classInfo;
304         PropertyInfo[string] map;
305         foreach(i, p; properties) {
306             p._entity = this;
307             map[p.propertyName] = p;
308             if (p.key) {
309                 _keyIndex = cast(int)i;
310                 _keyProperty = p;
311             }
312         }
313         this._propertyMap = map;
314         enforceHelper!MappingException(keyProperty !is null || embeddable, "No key specified for non-embeddable entity " ~ name);
315     }
316     /// returns key value as Variant from entity instance
317     Variant getKey(Object obj) const { return keyProperty.getFunc(obj); }
318     /// returns key value as Variant from data set
319     Variant getKey(DataSetReader r, int startColumn) const { return r.getVariant(startColumn + keyProperty.columnOffset); }
320     /// sets key value from Variant
321     void setKey(Object obj, Variant value) const { keyProperty.setFunc(obj, value); }
322     /// returns property info for key property
323     const(PropertyInfo) getKeyProperty() const { return keyProperty; }
324     /// checks if primary key is set (for non-nullable member types like int or long, 0 is considered as non-set)
325     bool isKeySet(Object obj) const { return keyProperty.keyIsSetFunc(obj); }
326     /// checks if primary key is set (for non-nullable member types like int or long, 0 is considered as non-set)
327     bool isKeyNull(DataSetReader r, int startColumn) const { return r.isNull(startColumn + keyProperty.columnOffset); }
328     /// checks if property value is null
329     bool isNull(Object obj) const { return keyProperty.isNullFunc(obj); }
330     /// returns property value as Variant
331     Variant getPropertyValue(Object obj, string propertyName) const { return findProperty(propertyName).getFunc(obj); }
332     /// sets property value from Variant
333     void setPropertyValue(Object obj, string propertyName, Variant value) const { return findProperty(propertyName).setFunc(obj, value); }
334     /// returns all properties as array
335     const (PropertyInfo[]) getProperties() const { return properties; }
336     /// returns map of property name to property metadata
337     const (PropertyInfo[string]) getPropertyMap() const { return _propertyMap; }
338     /// returns number of properties
339     ulong getPropertyCount() const { return properties.length; }
340     /// returns number of properties
341     ulong getPropertyCountExceptKey() const { return properties.length - 1; }
342 
343     @property size_t length() const { return properties.length; }
344 
345     const(PropertyInfo) opIndex(int index) const {
346         return properties[index];
347     }
348 
349     const(PropertyInfo) opIndex(string propertyName) const {
350         return findProperty(propertyName);
351     }
352 
353     /// returns property by index
354     const(PropertyInfo) getProperty(int propertyIndex) const { return properties[propertyIndex]; }
355     /// returns property by name, throws exception if not found
356     const(PropertyInfo) findProperty(string propertyName) const { try { return _propertyMap[propertyName]; } catch (Throwable e) { throw new MappingException("No property " ~ propertyName ~ " found in entity " ~ name); } }
357     /// create instance of entity object (using default constructor)
358     Object createEntity() const { return Object.factory(classInfo.name); }
359 
360     void copyAllProperties(Object to, Object from) const {
361         foreach(pi; this)
362             pi.copyFieldFunc(to, from);
363     }
364 }
365 
366 class JoinTableInfo {
367     package string _tableName;
368     @property string tableName() const { return _tableName; }
369     package string _column1;
370     @property string column1() const { return _column1; }
371     package string _column2;
372     @property string column2() const { return _column2; }
373     package EntityInfo _thisEntity;
374     @property const (EntityInfo) thisEntity() const { return _thisEntity; }
375     package EntityInfo _otherEntity;
376     @property const (EntityInfo) otherEntity() const { return _otherEntity; }
377     this(string tableName, string column1, string column2) {
378         this._tableName = tableName;
379         this._column1 = column1;
380         this._column2 = column2;
381     }
382     /// set entities, and replace missing parameters with default generated values
383     package void setEntities(const EntityInfo thisEntity, const EntityInfo otherEntity) {
384         assert(thisEntity !is null);
385         assert(otherEntity !is null);
386         this._thisEntity = cast(EntityInfo)thisEntity;
387         this._otherEntity = cast(EntityInfo)otherEntity;
388         // table name is constructed from names of two entities delimited with underscore, sorted in alphabetical order, with appended suffix 's': entity1_entity2s
389         // (to get same table name on two sides)
390         string entity1 = camelCaseToUnderscoreDelimited(thisEntity.name < otherEntity.name ? thisEntity.name : otherEntity.name);
391         string entity2 = camelCaseToUnderscoreDelimited(thisEntity.name < otherEntity.name ? otherEntity.name : thisEntity.name);
392         _tableName = _tableName !is null ? _tableName : entity1 ~ "_" ~ entity2 ~ "s";
393         // columns are entity name (CamelCase to camel_case
394         _column1 = _column1 !is null ? _column1 : camelCaseToUnderscoreDelimited(thisEntity.name) ~ "_fk";
395         _column2 = _column2 !is null ? _column2 : camelCaseToUnderscoreDelimited(otherEntity.name) ~ "_fk";
396     }
397     static string generateJoinTableCode(string table, string column1, string column2) {
398         return "new JoinTableInfo(" ~ quoteString(table) ~ ", " ~ quoteString(column1) ~ ", " ~ quoteString(column2) ~ ")";
399     }
400     
401     string getInsertSQL(const Dialect dialect) const {
402         return "INSERT INTO " ~ dialect.quoteIfNeeded(_tableName) ~ "(" ~ dialect.quoteIfNeeded(_column1) ~ ", " ~ dialect.quoteIfNeeded(column2) ~ ") VALUES ";
403     }
404 
405     string getOtherKeySelectSQL(const Dialect dialect, string thisKeySQL) const {
406         return "SELECT " ~ dialect.quoteIfNeeded(column2) ~ " FROM " ~ dialect.quoteIfNeeded(_tableName) ~ " WHERE " ~ dialect.quoteIfNeeded(_column1) ~ "=" ~ thisKeySQL;
407     }
408 
409     string getInsertSQL(const Dialect dialect, string thisKeySQL, string[] otherKeysSQL) const {
410         string list;
411         foreach(otherKeySQL; otherKeysSQL) {
412             if (list.length > 0)
413                 list ~= ", ";
414             list ~= "(" ~ thisKeySQL ~ ", " ~ otherKeySQL ~ ")";
415         }
416         return getInsertSQL(dialect) ~ list;
417     }
418 
419     string getDeleteSQL(const Dialect dialect, string thisKeySQL, string[] otherKeysSQL) const {
420         string sql = "DELETE FROM " ~ dialect.quoteIfNeeded(_tableName) ~ " WHERE " ~ dialect.quoteIfNeeded(_column1) ~ "=" ~ thisKeySQL ~ " AND " ~ dialect.quoteIfNeeded(_column2) ~ " IN ";
421         string list;
422         foreach(otherKeySQL; otherKeysSQL) {
423             if (list.length > 0)
424                 list ~= ", ";
425             list ~= otherKeySQL;
426         }
427         return sql ~ "(" ~ list ~ ")";
428     }
429 }
430 
431 string quoteString(string s) {
432     return s is null ? "null" : "\"" ~ s ~ "\"";
433 }
434 
435 string quoteBool(bool b) {
436     return b ? "true" : "false";
437 }
438 
439 string capitalizeFieldName(immutable string name) {
440     if (name[0] == '_')
441         return toUpper(name[1..2]) ~ name[2..$];
442     else
443         return toUpper(name[0..1]) ~ name[1..$];
444 }
445 
446 /// lowercases first letter
447 string classNameToPropertyName(immutable string name) {
448     return toLower(name[0..1]) ~ name[1..$];
449 }
450 
451 string getterNameToFieldName(immutable string name) {
452     if (name[0..3] == "get")
453         return toLower(name[3..4]) ~ name[4..$];
454     if (name[0..2] == "is")
455         return toLower(name[2..3]) ~ name[3..$];
456     return "_" ~ name;
457 }
458 
459 string getterNameToSetterName(immutable string name) {
460     if (name[0..3] == "get")
461         return "set" ~ name[3..$]; // e.g. getValue() -> setValue()
462     if (name[0..2] == "is")
463         return "set" ~ toUpper(name[0..1]) ~ name[1..$]; // e.g.  isDefault()->setIsDefault()
464     return "_" ~ name;
465 }
466 
467 /// converts camel case MyEntityName to my_entity_name
468 string camelCaseToUnderscoreDelimited(immutable string s) {
469     string res;
470     bool lastLower = false;
471     foreach(ch; s) {
472         if (ch >= 'A' && ch <= 'Z') {
473             if (lastLower) {
474                 lastLower = false;
475                 res ~= "_";
476             }
477             res ~= std.ascii.toLower(ch);
478         } else if (ch >= 'a' && ch <= 'z') {
479             lastLower = true;
480             res ~= ch;
481         } else {
482             res ~= ch;
483         }
484     }
485     return res;
486 }
487 
488 unittest {
489     static assert(camelCaseToUnderscoreDelimited("User") == "user");
490     static assert(camelCaseToUnderscoreDelimited("MegaTableName") == "mega_table_name");
491 }
492 
493 /// returns true if class member has at least one known property level annotation (@Column, @Id, @Generated)
494 template hasHibernatedPropertyAnnotation(T, string m) {
495     enum bool hasHibernatedPropertyAnnotation = hasOneOfMemberAnnotations!(T, m, Id, Column, OneToOne, ManyToOne, ManyToMany, OneToMany, Generated, Generator);
496 }
497 
498 bool hasHibernatedClassOrPropertyAnnotation(T)() {
499     static if (hasOneOfAnnotations!(T, Entity, Embeddable, Table)) {
500         return true;
501     } else {
502         auto hasAnnotation = false;
503         foreach (m; __traits(allMembers, T)) {
504             static if (__traits(compiles, (typeof(__traits(getMember, T, m))))){
505                 static if (__traits(getProtection, __traits(getMember, T, m)) == "public") {
506                     static if (hasHibernatedPropertyAnnotation!(T, m)) {
507                         hasAnnotation = true;
508                         break;
509                     }
510                 }
511             }
512         }
513         return hasAnnotation;
514     }
515 }
516 
517 bool hasAnyKeyPropertyAnnotation(T)() {
518     auto hasAny = false;
519     foreach (m; __traits(allMembers, T)) {
520         static if (__traits(compiles, (typeof(__traits(getMember, T, m))))){
521             static if (__traits(getProtection, __traits(getMember, T, m)) == "public") {
522                 static if (hasOneOfMemberAnnotations!(T, m, Id, Generated, Generator))
523                     hasAny = true;
524                     break;
525             }
526         }
527     }
528     return hasAny;
529 }
530 
531 /// returns true if class has one of specified anotations
532 bool hasOneOfAnnotations(T : Object, A...)() {
533     auto hasOne = false;
534     foreach(a; A) {
535         static if (hasAnnotation!(T, a)) {
536             hasOne = true;
537             break;
538         }
539     }
540     return hasOne;
541 }
542 
543 /// returns true if class member has one of specified anotations
544 bool hasOneOfMemberAnnotations(T : Object, string m, A...)() {
545     bool res = false;
546     foreach(a; A) {
547         static if (hasMemberAnnotation!(T, m, a)) {
548             res = true;
549             break;
550         }
551     }
552     return res;
553 }
554 
555 /// returns true if class has specified anotations
556 bool hasAnnotation(T, A)() {
557     bool res = false;
558     foreach(a; __traits(getAttributes, T)) {
559         static if (is(typeof(a) == A) || a.stringof == A.stringof) {
560             res = true;
561             break;
562         }
563     }
564     return res;
565 }
566 
567 bool isGetterFunction(alias overload, string methodName)() {
568     //pragma(msg, "isGetterFunction " ~ methodName ~ " " ~ typeof(overload).stringof);
569     static if (is(typeof(overload) == function)) {
570         //pragma(msg, "is function " ~ methodName ~ " " ~ typeof(overload).stringof);
571         static if (ParameterTypeTuple!(overload).length == 0) {
572             //pragma(msg, "no params " ~ methodName ~ " " ~ typeof(overload).stringof);
573             static if (functionAttributes!overload & FunctionAttribute.property) {
574                 //pragma(msg, "is property");
575                 //writeln("is property or starts with get or is");
576                 return true;
577             } else if (methodName.startsWith("get") || methodName.startsWith("get")) {
578                 //pragma(msg, "is getter");
579                 //writeln("is property or starts with get or is");
580                 return true;
581             } else {
582                 return false;
583             }
584         } else {
585             return false;
586         }
587     } else {
588         return false;
589     }
590 }
591 
592 /// returns true if class member has specified anotations
593 bool hasMemberAnnotation(T, string m, A)() {
594     bool res = false;
595     static if (is(typeof(__traits(getMember, T, m)) == function)) {
596         // function: check overloads
597     Louter:
598         foreach(overload; MemberFunctionsTuple!(T, m)) {
599             static if (isGetterFunction!(overload, m)) {
600                 foreach(a; __traits(getAttributes, overload)) {
601                     static if (is(typeof(a) == A) || a.stringof == A.stringof) {
602                         res = true;
603                         break Louter;
604                     }
605                 }
606             }
607         }
608     } else {
609         foreach(a; __traits(getAttributes, __traits(getMember,T,m))) {
610             static if (is(typeof(a) == A) || a.stringof == A.stringof) {
611                 res = true;
612                 break;
613             }
614         }
615     }
616     return res;
617 }
618 
619 /// returns entity name for class type
620 string getEntityName(T : Object)() {
621 //  foreach (a; __traits(getAttributes, T)) {
622 //      static if (is(typeof(a) == Entity)) {
623 //          return a.name;
624 //      }
625 //      static if (a.stringof == Entity.stringof) {
626 //          return T.stringof;
627 //      }
628 //  }
629     return T.stringof;
630 }
631 
632 /// returns table name for class type
633 string getTableName(T : Object)() {
634     string name = camelCaseToUnderscoreDelimited(T.stringof);
635     foreach (a; __traits(getAttributes, T)) {
636         static if (is(typeof(a) == Table)) {
637             name = a.name;
638             break;
639         }
640     }
641     return name;
642 }
643 
644 string applyDefault(string s, string defaultValue) {
645     return s != null && s.length > 0 ? s : defaultValue;
646 }
647 
648 string getColumnName(T, string m)() {
649     immutable string defValue = camelCaseToUnderscoreDelimited(getPropertyName!(T,m));
650     string name = defValue;
651     static if (is(typeof(__traits(getMember, T, m)) == function)) {
652         // function: check overloads
653      Louter:
654         foreach(overload; MemberFunctionsTuple!(T, m)) {
655             static if (isGetterFunction!(overload, m)) {
656                 foreach(a; __traits(getAttributes, overload)) {
657                     static if (is(typeof(a) == Column)) {
658                         name = applyDefault(a.name, defValue);
659                         break Louter;
660                     }
661                 }
662             }
663         }
664     } else {
665         foreach(a; __traits(getAttributes, __traits(getMember,T,m))) {
666             static if (is(typeof(a) == Column)) {
667                 name = applyDefault(a.name, defValue);
668                 break;
669             }
670         }
671     }
672     return name;
673 }
674 
675 string getGeneratorCode(T, string m)() {
676     string code = null;
677     foreach (a; __traits(getAttributes, __traits(getMember,T,m))) {
678         static if (is(typeof(a) == Generator)) {
679             static assert(a.code != null && a.code != "", "@Generator doesn't have code specified");
680             code = a.code;
681             break;
682         }
683         static if (a.stringof == Generator.stringof) {
684             static assert(false, "@Generator doesn't have code specified");
685         }
686     }
687     return code;
688 }
689 
690 string getJoinColumnName(T, string m)() {
691     string name = null;
692     immutable string defValue = camelCaseToUnderscoreDelimited(getPropertyName!(T,m)()) ~ "_fk";
693     static if (is(typeof(__traits(getMember, T, m)) == function)) {
694         // function: check overloads
695     Louter:
696         foreach(overload; MemberFunctionsTuple!(T, m)) {
697             static if (isGetterFunction!(overload, m)) {
698                 foreach(a; __traits(getAttributes, overload)) {
699                     static if (is(typeof(a) == JoinColumn)) {
700                         name = applyDefault(a.name, defValue);
701                         break Louter;
702                     } else static if (a.stringof == JoinColumn.stringof) {
703                         name = defValue;   
704                         break Louter;
705                     }
706                 }
707             }
708         }
709     } else {
710         foreach(a; __traits(getAttributes, __traits(getMember,T,m))) {
711             static if (is(typeof(a) == JoinColumn)) {
712                 name = applyDefault(a.name, defValue);
713                 break;
714             } else static if (a.stringof == JoinColumn.stringof) {
715                 name = defValue;
716                 break;
717             }
718         }
719     }
720     return name;
721 }
722 
723 string getUniqueIndexName(T, string m)() {
724     string name = null;
725     immutable string defValue = camelCaseToUnderscoreDelimited(getEntityName!T) ~ "_" ~ camelCaseToUnderscoreDelimited(getPropertyName!(T,m)()) ~ "_index";
726     static if (is(typeof(__traits(getMember, T, m)) == function)) {
727         // function: check overloads
728     Louter:
729         foreach(overload; MemberFunctionsTuple!(T, m)) {
730             static if (isGetterFunction!(overload, m)) {
731                 foreach(a; __traits(getAttributes, overload)) {
732                     static if (is(typeof(a) == UniqueKey)) {
733                         name = applyDefault(a.name, defValue);
734                         break Louter;
735                     } else static if (a.stringof == UniqueKey.stringof) {
736                         name = defValue;
737                         break Louter;
738                     }
739                 }
740             }
741         }
742     } else {
743         foreach(a; __traits(getAttributes, __traits(getMember,T,m))) {
744             static if (is(typeof(a) == UniqueKey)) {
745                 name = applyDefault(a.name, defValue);
746                 break;
747             } else static if (a.stringof == UniqueKey.stringof) {
748                 name = defValue;
749                 break;
750             }
751         }
752     }
753     return name;
754 }
755 
756 string getJoinTableName(T, string m)() {
757     string name = null;
758     static if (is(typeof(__traits(getMember, T, m)) == function)) {
759         // function: check overloads
760     Louter:
761         foreach(overload; MemberFunctionsTuple!(T, m)) {
762             static if (isGetterFunction!(overload, m)) {
763                 foreach(a; __traits(getAttributes, overload)) {
764                     static if (is(typeof(a) == JoinTable)) {
765                         name = emptyStringToNull(a.joinTableName);
766                         break Louter;
767                     }
768                 }
769             }
770         }
771     } else {
772         foreach(a; __traits(getAttributes, __traits(getMember,T,m))) {
773             static if (is(typeof(a) == JoinTable)) {
774                 name = emptyStringToNull(a.joinTableName);
775                 break;
776             }
777         }
778     }
779     return name;
780 }
781 
782 string getJoinTableColumn1(T, string m)() {
783     string column = null;
784     foreach (a; __traits(getAttributes, __traits(getMember,T,m))) {
785         static if (is(typeof(a) == JoinTable)) {
786             column = emptyStringToNull(a.joinColumn1);
787             break;
788         }
789     }
790     return column;
791 }
792 
793 string getJoinTableColumn2(T, string m)() {
794     string column = null;
795     foreach (a; __traits(getAttributes, __traits(getMember,T,m))) {
796         static if (is(typeof(a) == JoinTable)) {
797             column = emptyStringToNull(a.joinColumn2);
798             break;
799         }
800     }
801     return column;
802 }
803 
804 string emptyStringToNull(string s) {
805     return (s is null || s.length == 0) ? null : s;
806 }
807 
808 string getOneToOneReferencedPropertyName(T, string m)() {
809     string propertyName = null;
810     foreach (a; __traits(getAttributes, __traits(getMember,T,m))) {
811         static if (is(typeof(a) == OneToOne)) {
812             propertyName = emptyStringToNull(a.name);
813             break;
814         }
815         static if (a.stringof == OneToOne.stringof) {
816             propertyName = null;
817             break;
818         }
819     }
820     return propertyName;
821 }
822 
823 /**
824 T is class/entity
825 m is member of T eg T.m; m is a collection/array. The ElementType of the collection/array has a ref to T.
826 Try to figure out the field name in the type of m which is of type T.
827 When m has a attribute of OneToMany with a name, the atribute.name is used.
828 */
829 string getOneToManyReferencedPropertyName(T, string m)() {
830     // first check there is a attribute @OneToMany with a non null name, if so use it!
831     foreach( a; __traits( getAttributes, __traits( getMember, T, m ) ) ) {
832         static if( is( typeof(a) == OneToMany ) && a.name != null && a.name.length != 0 ) {
833             return a.name;
834         }
835     }
836     // No attrib or no name, try to deduce the field name from the type of T's field m
837     alias memberFieldType = typeof(__traits(getMember, T, m));
838     static if( is( memberFieldType == LazyCollection!TAL, TAL ))
839     {
840         alias refererType = TAL;
841     }
842     else
843     {
844         import std.range : ElementType;
845         alias refererType = ElementType!memberFieldType;
846     }
847     // test T has single occurance in refererType
848     static if (__VERSION__ < 2074) {
849         import std.traits : FieldTypeTuple, Filter; // Filter used to be in std.traits
850     } else {
851         import std.traits : FieldTypeTuple;
852         import std.meta : Filter;
853     }
854     alias refererFields = FieldTypeTuple!refererType;
855     enum bool isSameType(U) = is( T == U ) || is ( Lazy!T == U );
856     alias refererFieldsofTypeT = Filter!( isSameType, refererFields );
857     // assert there is exactly one field with type T in refererFields
858     // when there is more than one use explicit attributes for each field eg: OneToMany( "field name first referer" ).. OneToMany( "field name second referer" )..
859     static assert( refererFieldsofTypeT.length == 1, "auto deduction of OneToMany referencedPropertyName for " ~ T.stringof ~ "." ~ m ~ " failed: ElementType of " ~ refererType.stringof ~ "[] has " ~ refererFieldsofTypeT.length.stringof ~ " of fields " ~ T.stringof ~ ". (Use explicit OneToMany( fieldname in " ~ refererType.stringof ~ " ) annotations for multiple referers.)" );
860     string res = null;
861     foreach( mf; __traits( allMembers, refererType ) ) {
862         static if( is( typeof(__traits(getMember, refererType, mf)) == T ) ) {
863             res = mf;
864             break;
865         }
866     }
867     return res;
868 }
869 
870 int getColumnLength(T, string m)() {
871     auto length = 0;
872     static if (is(typeof(__traits(getMember, T, m)) == function)) {
873         // function: check overloads
874     Louter:
875         foreach(overload; MemberFunctionsTuple!(T, m)) {
876             static if (isGetterFunction!(overload, m)) {
877                 foreach(a; __traits(getAttributes, overload)) {
878                     static if (is(typeof(a) == Column)) {
879                         length = a.length;
880                         break Louter;
881                     }
882                 }
883             }
884         }
885     } else {
886         foreach(a; __traits(getAttributes, __traits(getMember,T,m))) {
887             static if (is(typeof(a) == Column)) {
888                 length = a.length;
889                 break;
890             }
891         }
892     }
893     return length;
894 }
895 
896 string getPropertyName(T, string m)() {
897     alias ti = typeof(__traits(getMember, T, m));
898 
899     static if (is(ti == function)) {
900         return getterNameToFieldName(m);
901     } else
902         return m;
903 }
904 
905 enum PropertyMemberKind : int {
906     FIELD_MEMBER,      // int field;
907     GETTER_MEMBER,     // getField() + setField() or isField() and setField()
908     PROPERTY_MEMBER,   // @property T field() { ... } + @property xxx field(T value) { ... }
909     LAZY_MEMBER,       // Lazy!Object field;
910     UNSUPPORTED_MEMBER,// 
911 }
912 
913 bool hasPercentSign(immutable string str) {
914     foreach(ch; str) {
915         if (ch == '%')
916             return true;
917     }
918     return false;
919 }
920 
921 int percentSignCount(immutable string str) {
922     string res;
923     foreach(ch; str) {
924         if (ch == '%')
925             res ~= "%";
926     }
927     return cast(int)res.length;
928 }
929 
930 string substituteParam(immutable string fmt, immutable string value) {
931     if (hasPercentSign(fmt))
932         return format(fmt, value);
933     else
934         return fmt;
935 }
936 
937 //string substituteIntParam(immutable string fmt, immutable int value) {
938 //    int percentPos = -1;
939 //    for (int i=0; i<fmt.length; i++) {
940 //        if (fmt[i] == '%') {
941 //            percentPos = i;
942 //        }
943 //
944 //    }
945 //    if (percentPos < 0)
946 //        return fmt;
947 //    return fmt[0 .. percentPos] ~ "1024" ~ fmt[percentPos + 2 .. $]; //to!string(value)
948 ////    string res;
949 ////    bool skipNext = false;
950 ////
951 ////    foreach(ch; fmt) {
952 ////        if (ch == '%') {
953 ////            res ~= "1024"; //to!string(value);
954 ////            skipNext = true;
955 ////        } else if (!skipNext) {
956 ////            res ~= ch;
957 ////            skipNext = false;
958 ////        }
959 ////    }
960 ////    return res;
961 //    // following code causes error in DMD
962 ////    if (hasPercentSign(fmt))
963 ////        return format(fmt, value);
964 ////    else
965 ////        return fmt;
966 //}
967 
968 string substituteParamTwice(immutable string fmt, immutable string value) {
969     immutable int paramCount = cast(int)percentSignCount(fmt);
970     if (paramCount == 1)
971         return format(fmt, value);
972     else if (paramCount == 2)
973         return format(fmt, value, value);
974     else
975         return fmt;
976 }
977 
978 static immutable string[] PropertyMemberKind_ReadCode = 
979     [
980         "entity.%s",
981         "entity.%s()",
982         "entity.%s",
983         "entity.%s()",
984         "dummy"
985     ];
986 
987 PropertyMemberKind getPropertyMemberKind(T : Object, string m)() {
988     auto memberKind = PropertyMemberKind.UNSUPPORTED_MEMBER;
989     alias ti = typeof(__traits(getMember, T, m));
990 
991     static if (is(ti == function)) {
992         // interate through all overloads
993         //return checkGetterOverload!(T, m);
994         foreach(overload; MemberFunctionsTuple!(T, m)) {
995             static if (ParameterTypeTuple!(overload).length == 0) {
996                 static if (functionAttributes!overload & FunctionAttribute.property) {
997                     memberKind = PropertyMemberKind.PROPERTY_MEMBER;
998                     break;
999                 }
1000                 else static if (m.startsWith("get") || m.startsWith("is")) {
1001                     memberKind = PropertyMemberKind.GETTER_MEMBER;
1002                     break;
1003                 }
1004             }
1005         }
1006     } else {
1007         static if (isLazyInstance!(ti)) {
1008             memberKind = PropertyMemberKind.LAZY_MEMBER;
1009         } else {
1010             memberKind = PropertyMemberKind.FIELD_MEMBER;
1011         }
1012     }
1013     return memberKind;
1014 }
1015 
1016 string getPropertyEmbeddedEntityName(T : Object, string m)() {
1017     alias ti = typeof(__traits(getMember, T, m));
1018 
1019     static if (is(ti == function)) {
1020         static if (isImplicitlyConvertible!(ReturnType!(ti), Object)) {
1021             static assert(hasAnnotation!(ReturnType!(ti), Embeddable), "@Embedded property class should have @Embeddable annotation");
1022             return getEntityName!(ReturnType!(ti));
1023         } else
1024             static assert(false, "@Embedded property can be only class with @Embeddable annotation");
1025     } else {
1026         static if (isImplicitlyConvertible!(ti, Object)) {
1027             static assert(hasAnnotation!(ti, Embeddable), "@Embedded property class should have @Embeddable annotation");
1028             return getEntityName!ti;
1029         } else 
1030             static assert(false, "@Embedded property can be only class with @Embeddable annotation");
1031     }
1032 }
1033 
1034 template isLazyInstance(T) {
1035     static if (is(T x == Lazy!Args, Args...))
1036         enum bool isLazyInstance = true;
1037     else
1038         enum bool isLazyInstance = false;
1039 }
1040 
1041 template isLazyCollectionInstance(T) {
1042     static if (is(T x == LazyCollection!Args, Args...))
1043         enum bool isLazyCollectionInstance = true;
1044     else
1045         enum bool isLazyCollectionInstance = false;
1046 }
1047 
1048 template isLazyMember(T : Object, string m) {
1049     static if (is(typeof(__traits(getMember, T, m)) x == Lazy!Args, Args...))
1050         enum bool isLazyMember = true;
1051     else
1052         enum bool isLazyMember = false;
1053 }
1054 
1055 template isLazyCollectionMember(T : Object, string m) {
1056     static if (is(typeof(__traits(getMember, T, m)) x == LazyCollection!Args, Args...))
1057         enum bool isLazyCollectionMember = true;
1058     else
1059         enum bool isLazyCollectionMember = false;
1060 }
1061 
1062 template isObject(T) {
1063     enum bool isObject = (__traits(compiles, isImplicitlyConvertible!(T, Object)) && isImplicitlyConvertible!(T, Object));
1064 }
1065 
1066 /// member is field or function or property with SomeClass type
1067 template isObjectMember(T : Object, string m) {
1068     alias ti = typeof(__traits(getMember, T, m));
1069 
1070     static if (is(ti == function)) {
1071         enum bool isObjectMember = isImplicitlyConvertible!(ReturnType!(ti), Object);
1072     } else {
1073         enum bool isObjectMember = isImplicitlyConvertible!(ti, Object);
1074     }
1075 }
1076 
1077 template hasPublicMember(T : Object, string m) {
1078     //pragma(msg, "hasPublicMember "~ T.stringof ~ ", " ~ m);
1079     static if (__traits(hasMember, T, m)) {
1080         enum bool hasPublicMember = __traits(compiles, __traits(getMember, T, m));//(__traits(getProtection, __traits(getMember, T, m)) == "public");
1081     } else {
1082         enum bool hasPublicMember = false;
1083     }
1084 }
1085 
1086 //unittest {
1087 //    class Foo {
1088 //        int id;
1089 //        void x();
1090 //    }
1091 //    static assert(hasPublicMember!(Foo, "id"));
1092 //    static assert(hasPublicMember!(Foo, "x"));
1093 //    static assert(!hasPublicMember!(Foo, "zzz"));
1094 //
1095 //}
1096 
1097 /// returns true if it's object field of Embeddable object type
1098 template isEmbeddedObjectMember(T : Object, string m) {
1099     static if (isObjectMember!(T, m)) {
1100         alias typeof(__traits(getMember, T, m)) ti;
1101         enum bool isEmbeddedObjectMember = hasAnnotation!(getReferencedInstanceType!ti, Embeddable);
1102     } else {
1103         enum bool isEmbeddedObjectMember = false;
1104     }
1105 }
1106 
1107 template hasPublicField(T : Object, string m) {
1108     static if (hasPublicMember!(T, m)) {
1109         enum bool hasPublicField = !is(typeof(__traits(getMember, T, m)) == function) && !is(typeof(__traits(getMember, T, m)) == delegate);
1110     } else {
1111         enum bool hasPublicField = false;
1112     }
1113 }
1114 
1115 template hasPublicFieldWithAnnotation(T : Object, string m) {
1116     static if (hasPublicField!(T, m)) {
1117         enum bool hasPublicFieldWithAnnotation = hasHibernatedPropertyAnnotation!(T, m);
1118     } else {
1119         enum bool hasPublicFieldWithAnnotation = false;
1120     }
1121 }
1122 
1123 /// returns true if one of overloads of member m of class T is property setter with specified value type
1124 bool hasWritePropretyForType(T: Object, string m, ParamType)() {
1125     auto hasProperty = false;
1126     foreach(overload; MemberFunctionsTuple!(T, m)) {
1127         static if (ParameterTypeTuple!(overload).length == 1) {
1128             static if (functionAttributes!overload & FunctionAttribute.property) {
1129                 hasProperty = is(ParameterTypeTuple!(overload)[0] == ParamType);
1130                 break;
1131             }
1132         }
1133     }
1134     return hasProperty;
1135 }
1136 
1137 /// returns true if member m of class T has both property getter and setter of the same type
1138 bool isReadWriteProperty(T: Object, string m)() {
1139     bool res = false;
1140     foreach(overload; MemberFunctionsTuple!(T, m)) {
1141         static if (ParameterTypeTuple!(overload).length == 0) {
1142             static if (functionAttributes!overload & FunctionAttribute.property) {
1143                 res = hasWritePropretyForType!(T, m, ReturnType!overload);
1144                 break;
1145             }
1146         }
1147     }
1148     return res;
1149 }
1150 
1151 /// check that member m exists in class T, and it's function with single parameter of type ti
1152 template isValidSetter(T : Object, string m, ParamType) {
1153     // it's public member
1154     static if (hasPublicMember!(T, m)) {
1155         // it's function with single parameter of proper type
1156         enum bool isValidSetter = is(typeof(__traits(getMember, T, m)) == function) &&
1157                 ParameterTypeTuple!(typeof(__traits(getMember, T, m))).length == 1 &&
1158                 is(ParameterTypeTuple!(typeof(__traits(getMember, T, m)))[0] == ParamType);
1159     } else {
1160         enum bool isValidSetter = false; 
1161     }
1162 }
1163 
1164 template isValidGetter(T : Object, string m) {
1165     // it's public member with get or is prefix
1166     static if ((m.startsWith("get") || m.startsWith("is")) && hasPublicMember!(T, m)) {
1167         alias ti = typeof(__traits(getMember, T, m));
1168         alias rti = ReturnType!ti;
1169 
1170         // it's function
1171         static if (is(typeof(__traits(getMember, T, m)) == function)) {
1172             // function has no parameters
1173             static if (ParameterTypeTuple!(typeof(__traits(getMember, T, m))).length == 0) {
1174                 // has paired setter function of the same type
1175                 static if (isValidSetter!(T, getterNameToSetterName(m), rti)) {
1176                     enum bool isValidGetter = true; 
1177                 } else {
1178                     enum bool isValidGetter = false;
1179                 }
1180             }
1181         } else {
1182             enum bool isValidGetter = false; 
1183         }
1184     } else {
1185         enum bool isValidGetter = false; 
1186     }
1187 }
1188 
1189 template isValidGetterWithAnnotation(T : Object, string m) {
1190     // it's public member with get or is prefix
1191     static if (isValidGetter!(T, m)) {
1192         enum bool isValidGetterWithAnnotation = hasHibernatedPropertyAnnotation!(T,m); 
1193     } else {
1194         enum bool isValidGetterWithAnnotation = false; 
1195     }
1196 }
1197 
1198 bool isMainMemberForProperty(T : Object, string m)() {
1199     // skip non-public members
1200     static if (hasPublicMember!(T, m)) {
1201         alias ti = typeof(__traits(getMember, T, m));
1202 
1203         immutable bool thisMemberHasAnnotation = hasHibernatedPropertyAnnotation!(T,m);
1204         static if (is(ti == function)) {
1205             // function or property
1206             static if (functionAttributes!ti & FunctionAttribute.property) {
1207                 // property
1208                 return isReadWriteProprety!(T, m);
1209             } else {
1210                 // getter function
1211                 // should have corresponding setter
1212                 static if (isValidGetter!(T,m)) {
1213                     // if any field with matching name is found, only one of them may have annotation
1214                     immutable bool annotatedField = hasPublicFieldWithAnnotation!(T, getterNameToFieldName(m)) || hasPublicFieldWithAnnotation!(T, "_" ~ getterNameToFieldName(m));
1215                     static assert(!annotatedField || !thisMemberHasAnnotation, "Both getter and corresponding field have annotations. Annotate only one of them.");
1216                     return !annotatedField;
1217                 } else {
1218                     // non-conventional name for getter or no setter
1219                     return false;
1220                 }
1221             }
1222         } else {
1223             // field
1224             //capitalizeFieldName
1225             immutable string gname = capitalizeFieldName(m);
1226             immutable bool hasAnnotadedGetter = isValidGetterWithAnnotation!(T, "get" ~ gname) || isValidGetterWithAnnotation!(T, "is" ~ gname);
1227             immutable bool hasGetter = isValidGetter!(T, "get" ~ gname) || isValidGetter!(T, "is" ~ gname);
1228             static assert (!thisMemberHasAnnotation || !hasAnnotadedGetter, "Both getter and corresponding field have annotations. Annotate only one of them.");
1229             return !hasAnnotadedGetter && (thisMemberHasAnnotation || !hasGetter);
1230         }
1231     } else {
1232         // member is not public
1233         return false;
1234     }
1235 }
1236 
1237 /// member is field or function or property returing SomeClass[] or LazyCollection!SomeClass
1238 template isCollectionMember(T : Object, string m) {
1239     alias ti = typeof(__traits(getMember, T, m));
1240 
1241     static if (is(ti == function)) {
1242         static if (is(ReturnType!(typeof(__traits(getMember, T, m))) x == LazyCollection!Args, Args...))
1243             enum bool isCollectionMember = true;
1244         else {
1245             //pragma(msg, typeof(__traits(getMember, T, m).init[0]));
1246             alias rti = ReturnType!ti;
1247             static if (isArray!rti && isImplicitlyConvertible!(typeof(rti.init[0]), Object))
1248                 enum bool isCollectionMember = true;
1249             else
1250                 enum bool isCollectionMember = false;
1251         }
1252     } else {
1253         static if (is(typeof(__traits(getMember, T, m)) x == LazyCollection!Args, Args...))
1254             enum bool isCollectionMember = true;
1255         else {
1256             //pragma(msg, typeof(__traits(getMember, T, m).init[0]));
1257             static if (isArray!(ti) && isImplicitlyConvertible!(typeof(__traits(getMember, T, m).init[0]), Object))
1258                 enum bool isCollectionMember = true;
1259             else
1260                 enum bool isCollectionMember = false;
1261         }
1262     }
1263 }
1264 
1265 unittest {
1266     class Foo {
1267         bool dummy;
1268     }
1269     struct Bar {
1270         bool dummy;
1271     }
1272     class MemberTest {
1273         bool simple;
1274         bool getSimple() { return simple; }
1275         int someInt;
1276         Long someLong;
1277         bool[] simples;
1278         bool[] getSimples() { return simples; }
1279         @property bool[] simpless() { return simples; }
1280         Foo foo;
1281         Foo getFoo() { return foo; }
1282         @property Foo fooo() { return foo; }
1283         Foo[] foos;
1284         Foo[] getFoos() { return foos; }
1285         @property Foo[] fooos() { return foos; }
1286         LazyCollection!Foo lfoos;
1287         ref LazyCollection!Foo lgetFoos() { return lfoos; }
1288         @property ref LazyCollection!Foo lfooos() { return lfoos; }
1289     }
1290     static assert(getColumnName!(MemberTest, "simple") == "simple");
1291     static assert(getColumnName!(MemberTest, "getSimple") == "simple");
1292     static assert(isObject!Foo);
1293     static assert(!isObject!Bar);
1294     static assert(!isObjectMember!(MemberTest, "simple"));
1295     static assert(!isObjectMember!(MemberTest, "simples"));
1296     static assert(!isObjectMember!(MemberTest, "getSimples"));
1297     static assert(!isObjectMember!(MemberTest, "simpless"));
1298     static assert(!isCollectionMember!(MemberTest, "simples"));
1299     static assert(!isCollectionMember!(MemberTest, "getSimples"));
1300     static assert(!isCollectionMember!(MemberTest, "simpless"));
1301     static assert(isObjectMember!(MemberTest, "foo"));
1302     static assert(isObjectMember!(MemberTest, "getFoo"));
1303     static assert(isObjectMember!(MemberTest, "fooo"));
1304     static assert(!isCollectionMember!(MemberTest, "simple"));
1305     static assert(!isCollectionMember!(MemberTest, "foo"));
1306     static assert(!isCollectionMember!(MemberTest, "getFoo"));
1307     static assert(!isCollectionMember!(MemberTest, "fooo"));
1308     static assert(isCollectionMember!(MemberTest, "foos"));
1309     static assert(isCollectionMember!(MemberTest, "getFoos"));
1310     static assert(isCollectionMember!(MemberTest, "fooos"));
1311     static assert(isCollectionMember!(MemberTest, "lfoos"));
1312     static assert(isCollectionMember!(MemberTest, "lgetFoos"));
1313     static assert(isCollectionMember!(MemberTest, "lfooos"));
1314     static assert(isSupportedSimpleType!(MemberTest, "simple"));
1315     static assert(!isSupportedSimpleType!(MemberTest, "foo"));
1316     static assert(isSupportedSimpleType!(MemberTest, "someInt"));
1317     static assert(isSupportedSimpleType!(MemberTest, "someLong"));
1318 }
1319 
1320 template getLazyInstanceType(T) {
1321     static if (is(T x == Lazy!Args, Args...))
1322         alias getLazyInstanceType = Args[0];
1323     else {
1324         static assert(false, "Not a Lazy! instance");
1325     }
1326 }
1327 
1328 template getLazyCollectionInstanceType(T) {
1329     static if (is(T x == LazyCollection!Args, Args...))
1330         alias getLazyInstanceType = Args[0];
1331     else {
1332         static assert(false, "Not a LazyCollection! instance");
1333     }
1334 }
1335 
1336 template getReferencedInstanceType(T) {
1337     //pragma(msg, T.stringof);
1338     static if (is(T == delegate)) {
1339         //pragma(msg, "is delegate");
1340         static if (isImplicitlyConvertible!(ReturnType!(T), Object)) {
1341             alias getReferencedInstanceType = ReturnType!(T);
1342         } else
1343             static assert(false, "@OneToOne, @ManyToOne, @OneToMany, @ManyToMany property can be only class or Lazy!class");
1344     } else static if (is(T == function)) {
1345         //pragma(msg, "is function");
1346         static if (isImplicitlyConvertible!(ReturnType!(T), Object)) {
1347             alias getReferencedInstanceType = ReturnType!(T);
1348         } else {
1349             static if (is(ReturnType!(T) x == Lazy!Args, Args...))
1350                 alias getReferencedInstanceType = Args[0];
1351             else
1352                 static assert(false, "Type cannot be used as relation " ~ T.stringof);
1353         }
1354     } else {
1355         //pragma(msg, "is not function");
1356         static if (is(T x == LazyCollection!Args, Args...)) {
1357             alias getReferencedInstanceType = Args[0];
1358         } else {
1359             static if (is(T x == Lazy!Args, Args...)) {
1360                 alias getReferencedInstanceType = Args[0];
1361             } else {
1362                 static if (isArray!(T)) {
1363                     static if (isImplicitlyConvertible!(typeof(T.init[0]), Object)) {
1364                         //pragma(msg, "isImplicitlyConvertible!(T, Object)");
1365                         alias getReferencedInstanceType = typeof(T.init[0]);
1366                     } else {
1367                         static assert(false, "Type cannot be used as relation " ~ T.stringof);
1368                     }
1369                 } else static if (isImplicitlyConvertible!(T, Object)) {
1370                     //pragma(msg, "isImplicitlyConvertible!(T, Object)");
1371                     alias getReferencedInstanceType = T;
1372                 } else static if (isImplicitlyConvertible!(T, Object[])) {
1373                     //pragma(msg, "isImplicitlyConvertible!(T, Object)");
1374                     alias getReferencedInstanceType = T;
1375                 } else {
1376                     static assert(false, "Type cannot be used as relation " ~ T.stringof);
1377                 }
1378             }
1379         }
1380     }
1381 }
1382 
1383 string getPropertyReferencedEntityName(T : Object, string m)() {
1384     alias ti = typeof(__traits(getMember, T, m));
1385     return getEntityName!(getReferencedInstanceType!ti);
1386 }
1387 
1388 string getPropertyEmbeddedClassName(T : Object, string m)() {
1389     alias ti = typeof(__traits(getMember, T, m));
1390 
1391     static if (is(ti == function)) {
1392         static if (isImplicitlyConvertible!(ReturnType!(ti), Object)) {
1393             static assert(hasAnnotation!(ReturnType!(ti), Embeddable), "@Embedded property class should have @Embeddable annotation");
1394             return fullyQualifiedName!(ReturnType!(ti));
1395         } else
1396             static assert(false, "@Embedded property can be only class with @Embeddable annotation");
1397     } else {
1398         static if (isImplicitlyConvertible!(ti, Object)) {
1399             static assert(hasAnnotation!(ti, Embeddable), "@Embedded property class should have @Embeddable annotation");
1400             return fullyQualifiedName!ti;
1401         } else 
1402             static assert(false, "@Embedded property can be only class with @Embeddable annotation");
1403     }
1404 }
1405 
1406 string getPropertyReferencedClassName(T : Object, string m)() {
1407     alias ti = typeof(__traits(getMember, T, m));
1408     return fullyQualifiedName!(getReferencedInstanceType!ti);
1409 }
1410 
1411 
1412 
1413 
1414 
1415 enum PropertyMemberType : int {
1416     BOOL_TYPE,    // bool
1417     BYTE_TYPE,    // byte
1418     SHORT_TYPE,   // short
1419     INT_TYPE,     // int
1420     LONG_TYPE,    // long
1421     UBYTE_TYPE,   // ubyte
1422     USHORT_TYPE,  // ushort
1423     UINT_TYPE,    // uint
1424     ULONG_TYPE,   // ulong
1425     NULLABLE_BYTE_TYPE,  // Nullable!byte
1426     NULLABLE_SHORT_TYPE, // Nullable!short
1427     NULLABLE_INT_TYPE,   // Nullable!int
1428     NULLABLE_LONG_TYPE,  // Nullable!long
1429     NULLABLE_UBYTE_TYPE, // Nullable!ubyte
1430     NULLABLE_USHORT_TYPE,// Nullable!ushort
1431     NULLABLE_UINT_TYPE,  // Nullable!uint
1432     NULLABLE_ULONG_TYPE, // Nullable!ulong
1433     FLOAT_TYPE,   // float
1434     DOUBLE_TYPE,   // double
1435     NULLABLE_FLOAT_TYPE, // Nullable!float
1436     NULLABLE_DOUBLE_TYPE,// Nullable!double
1437     STRING_TYPE,   // string
1438     NULLABLE_STRING_TYPE,   // nullable string - String struct
1439     DATETIME_TYPE, // std.datetime.DateTime
1440     DATE_TYPE, // std.datetime.Date
1441     TIME_TYPE, // std.datetime.TimeOfDay
1442     NULLABLE_DATETIME_TYPE, // Nullable!std.datetime.DateTime
1443     NULLABLE_DATE_TYPE, // Nullable!std.datetime.Date
1444     NULLABLE_TIME_TYPE, // Nullable!std.datetime.TimeOfDay
1445     BYTE_ARRAY_TYPE, // byte[]
1446     UBYTE_ARRAY_TYPE, // ubyte[]
1447 }
1448 
1449 template isSupportedSimpleType(T, string m) {
1450     alias ti = typeof(__traits(getMember, T, m));
1451     
1452     static if (is(ti == function)) {
1453         static if (is(ReturnType!(ti) == bool)) {
1454             enum bool isSupportedSimpleType = true;
1455         } else static if (is(ReturnType!(ti) == byte)) {
1456             enum bool isSupportedSimpleType = true;
1457         } else static if (is(ReturnType!(ti) == short)) {
1458             enum bool isSupportedSimpleType = true;
1459         } else static if (is(ReturnType!(ti) == int)) {
1460             enum bool isSupportedSimpleType = true;
1461         } else static if (is(ReturnType!(ti) == long)) {
1462             enum bool isSupportedSimpleType = true;
1463         } else static if (is(ReturnType!(ti) == ubyte)) {
1464             enum bool isSupportedSimpleType = true;
1465         } else static if (is(ReturnType!(ti) == ushort)) {
1466             enum bool isSupportedSimpleType = true;
1467         } else static if (is(ReturnType!(ti) == uint)) {
1468             enum bool isSupportedSimpleType = true;
1469         } else static if (is(ReturnType!(ti) == ulong)) {
1470             enum bool isSupportedSimpleType = true;
1471         } else static if (is(ReturnType!(ti) == float)) {
1472             enum bool isSupportedSimpleType = true;
1473         } else static if (is(ReturnType!(ti) == double)) {
1474             enum bool isSupportedSimpleType = true;
1475         } else static if (is(ReturnType!(ti) == Nullable!byte)) {
1476             enum bool isSupportedSimpleType = true;
1477         } else static if (is(ReturnType!(ti) == Nullable!short)) {
1478             enum bool isSupportedSimpleType = true;
1479         } else static if (is(ReturnType!(ti) == Nullable!int)) {
1480             enum bool isSupportedSimpleType = true;
1481         } else static if (is(ReturnType!(ti) == Nullable!long)) {
1482             enum bool isSupportedSimpleType = true;
1483         } else static if (is(ReturnType!(ti) == Nullable!ubyte)) {
1484             enum bool isSupportedSimpleType = true;
1485         } else static if (is(ReturnType!(ti) == Nullable!ushort)) {
1486             enum bool isSupportedSimpleType = true;
1487         } else static if (is(ReturnType!(ti) == Nullable!uint)) {
1488             enum bool isSupportedSimpleType = true;
1489         } else static if (is(ReturnType!(ti) == Nullable!ulong)) {
1490             enum bool isSupportedSimpleType = true;
1491         } else static if (is(ReturnType!(ti) == Nullable!float)) {
1492             enum bool isSupportedSimpleType = true;
1493         } else static if (is(ReturnType!(ti) == Nullable!double)) {
1494             enum bool isSupportedSimpleType = true;
1495         } else static if (is(ReturnType!(ti) == string)) {
1496             enum bool isSupportedSimpleType = true;
1497         } else static if (is(ReturnType!(ti) == hibernated.type.String)) {
1498             enum bool isSupportedSimpleType = true;
1499         } else static if (is(ReturnType!(ti) == DateTime)) {
1500             enum bool isSupportedSimpleType = true;
1501         } else static if (is(ReturnType!(ti) == Date)) {
1502             enum bool isSupportedSimpleType = true;
1503         } else static if (is(ReturnType!(ti) == TimeOfDay)) {
1504             enum bool isSupportedSimpleType = true;
1505         } else static if (is(ReturnType!(ti) == Nullable!DateTime)) {
1506             enum bool isSupportedSimpleType = true;
1507         } else static if (is(ReturnType!(ti) == Nullable!Date)) {
1508             enum bool isSupportedSimpleType = true;
1509         } else static if (is(ReturnType!(ti) == Nullable!TimeOfDay)) {
1510             enum bool isSupportedSimpleType = true;
1511         } else static if (is(ReturnType!(ti) == byte[])) {
1512             enum bool isSupportedSimpleType = true;
1513         } else static if (is(ReturnType!(ti) == ubyte[])) {
1514             enum bool isSupportedSimpleType = true;
1515         } else {
1516             enum bool isSupportedSimpleType = false;
1517         }
1518     } else static if (is(ti == bool)) {
1519         enum bool isSupportedSimpleType = true;
1520     } else static if (is(ti == byte)) {
1521         enum bool isSupportedSimpleType = true;
1522     } else static if (is(ti == short)) {
1523         enum bool isSupportedSimpleType = true;
1524     } else static if (is(ti == int)) {
1525         enum bool isSupportedSimpleType = true;
1526     } else static if (is(ti == long)) {
1527         enum bool isSupportedSimpleType = true;
1528     } else static if (is(ti == ubyte)) {
1529         enum bool isSupportedSimpleType = true;
1530     } else static if (is(ti == ushort)) {
1531         enum bool isSupportedSimpleType = true;
1532     } else static if (is(ti == uint)) {
1533         enum bool isSupportedSimpleType = true;
1534     } else static if (is(ti == ulong)) {
1535         enum bool isSupportedSimpleType = true;
1536     } else static if (is(ti == float)) {
1537         enum bool isSupportedSimpleType = true;
1538     } else static if (is(ti == double)) {
1539         enum bool isSupportedSimpleType = true;
1540     } else static if (is(ti == Nullable!byte)) {
1541         enum bool isSupportedSimpleType = true;
1542     } else static if (is(ti == Nullable!short)) {
1543         enum bool isSupportedSimpleType = true;
1544     } else static if (is(ti == Nullable!int)) {
1545         enum bool isSupportedSimpleType = true;
1546     } else static if (is(ti == Nullable!long)) {
1547         enum bool isSupportedSimpleType = true;
1548     } else static if (is(ti == Nullable!ubyte)) {
1549         enum bool isSupportedSimpleType = true;
1550     } else static if (is(ti == Nullable!ushort)) {
1551         enum bool isSupportedSimpleType = true;
1552     } else static if (is(ti == Nullable!uint)) {
1553         enum bool isSupportedSimpleType = true;
1554     } else static if (is(ti == Nullable!ulong)) {
1555         enum bool isSupportedSimpleType = true;
1556     } else static if (is(ti == Nullable!float)) {
1557         enum bool isSupportedSimpleType = true;
1558     } else static if (is(ti == Nullable!double)) {
1559         enum bool isSupportedSimpleType = true;
1560     } else static if (is(ti == string)) {
1561         enum bool isSupportedSimpleType = true;
1562     } else static if (is(ti == hibernated.type.String)) {
1563         enum bool isSupportedSimpleType = true;
1564     } else static if (is(ti == DateTime)) {
1565         enum bool isSupportedSimpleType = true;
1566     } else static if (is(ti == Date)) {
1567         enum bool isSupportedSimpleType = true;
1568     } else static if (is(ti == TimeOfDay)) {
1569         enum bool isSupportedSimpleType = true;
1570     } else static if (is(ti == Nullable!DateTime)) {
1571         enum bool isSupportedSimpleType = true;
1572     } else static if (is(ti == Nullable!Date)) {
1573         enum bool isSupportedSimpleType = true;
1574     } else static if (is(ti == Nullable!TimeOfDay)) {
1575         enum bool isSupportedSimpleType = true;
1576     } else static if (is(ti == byte[])) {
1577         enum bool isSupportedSimpleType = true;
1578     } else static if (is(ti == ubyte[])) {
1579         enum bool isSupportedSimpleType = true;
1580     } else {
1581         enum bool isSupportedSimpleType = false;
1582     }
1583 }
1584 
1585 PropertyMemberType getPropertyMemberType(T, string m)() {
1586     alias ti = typeof(__traits(getMember, T, m));
1587 
1588     static if (is(ti == function)) {
1589 		static if (is(ReturnType!(ti) == bool)) {
1590 			return PropertyMemberType.BOOL_TYPE;
1591 		} else static if (is(ReturnType!(ti) == byte)) {
1592             return PropertyMemberType.BYTE_TYPE;
1593         } else if (is(ReturnType!(ti) == short)) {
1594             return PropertyMemberType.SHORT_TYPE;
1595         } else if (is(ReturnType!(ti) == int)) {
1596             return PropertyMemberType.INT_TYPE;
1597         } else if (is(ReturnType!(ti) == long)) {
1598             return PropertyMemberType.LONG_TYPE;
1599         } else if (is(ReturnType!(ti) == ubyte)) {
1600             return PropertyMemberType.UBYTE_TYPE;
1601         } else if (is(ReturnType!(ti) == ushort)) {
1602             return PropertyMemberType.USHORT_TYPE;
1603         } else if (is(ReturnType!(ti) == uint)) {
1604             return PropertyMemberType.UINT_TYPE;
1605         } else if (is(ReturnType!(ti) == ulong)) {
1606             return PropertyMemberType.ULONG_TYPE;
1607         } else if (is(ReturnType!(ti) == float)) {
1608             return PropertyMemberType.FLOAT_TYPE;
1609         } else if (is(ReturnType!(ti) == double)) {
1610             return PropertyMemberType.DOUBLE_TYPE;
1611         } else if (is(ReturnType!(ti) == Nullable!byte)) {
1612             return PropertyMemberType.NULLABLE_BYTE_TYPE;
1613         } else if (is(ReturnType!(ti) == Nullable!short)) {
1614             return PropertyMemberType.NULLABLE_SHORT_TYPE;
1615         } else if (is(ReturnType!(ti) == Nullable!int)) {
1616             return PropertyMemberType.NULLABLE_INT_TYPE;
1617         } else if (is(ReturnType!(ti) == Nullable!long)) {
1618             return PropertyMemberType.NULLABLE_LONG_TYPE;
1619         } else if (is(ReturnType!(ti) == Nullable!ubyte)) {
1620             return PropertyMemberType.NULLABLE_UBYTE_TYPE;
1621         } else if (is(ReturnType!(ti) == Nullable!ushort)) {
1622             return PropertyMemberType.NULLABLE_USHORT_TYPE;
1623         } else if (is(ReturnType!(ti) == Nullable!uint)) {
1624             return PropertyMemberType.NULLABLE_UINT_TYPE;
1625         } else if (is(ReturnType!(ti) == Nullable!ulong)) {
1626             return PropertyMemberType.NULLABLE_ULONG_TYPE;
1627         } else if (is(ReturnType!(ti) == Nullable!float)) {
1628             return PropertyMemberType.NULLABLE_FLOAT_TYPE;
1629         } else if (is(ReturnType!(ti) == Nullable!double)) {
1630             return PropertyMemberType.NULLABLE_DOUBLE_TYPE;
1631         } else if (is(ReturnType!(ti) == string)) {
1632             return PropertyMemberType.STRING_TYPE;
1633         } else if (is(ReturnType!(ti) == hibernated.type.String)) {
1634             return PropertyMemberType.NULLABLE_STRING_TYPE;
1635         } else if (is(ReturnType!(ti) == DateTime)) {
1636             return PropertyMemberType.DATETIME_TYPE;
1637         } else if (is(ReturnType!(ti) == Date)) {
1638             return PropertyMemberType.DATE_TYPE;
1639         } else if (is(ReturnType!(ti) == TimeOfDay)) {
1640             return PropertyMemberType.TIME_TYPE;
1641         } else if (is(ReturnType!(ti) == Nullable!DateTime)) {
1642             return PropertyMemberType.NULLABLE_DATETIME_TYPE;
1643         } else if (is(ReturnType!(ti) == Nullable!Date)) {
1644             return PropertyMemberType.NULLABLE_DATE_TYPE;
1645         } else if (is(ReturnType!(ti) == Nullable!TimeOfDay)) {
1646             return PropertyMemberType.NULLABLE_TIME_TYPE;
1647         } else if (is(ReturnType!(ti) == byte[])) {
1648             return PropertyMemberType.BYTE_ARRAY_TYPE;
1649         } else if (is(ReturnType!(ti) == ubyte[])) {
1650             return PropertyMemberType.UBYTE_ARRAY_TYPE;
1651         } else {
1652             assert (false, "Member " ~ m ~ " of class " ~ T.stringof ~ " has unsupported type " ~ ti.stringof);
1653         }
1654 	} else if (is(ti == bool)) {
1655 		return PropertyMemberType.BOOL_TYPE;
1656     } else if (is(ti == byte)) {
1657         return PropertyMemberType.BYTE_TYPE;
1658     } else if (is(ti == short)) {
1659         return PropertyMemberType.SHORT_TYPE;
1660     } else if (is(ti == int)) {
1661         return PropertyMemberType.INT_TYPE;
1662     } else if (is(ti == long)) {
1663         return PropertyMemberType.LONG_TYPE;
1664     } else if (is(ti == ubyte)) {
1665         return PropertyMemberType.UBYTE_TYPE;
1666     } else if (is(ti == ushort)) {
1667         return PropertyMemberType.USHORT_TYPE;
1668     } else if (is(ti == uint)) {
1669         return PropertyMemberType.UINT_TYPE;
1670     } else if (is(ti == ulong)) {
1671         return PropertyMemberType.ULONG_TYPE;
1672     } else if (is(ti == float)) {
1673         return PropertyMemberType.FLOAT_TYPE;
1674     } else if (is(ti == double)) {
1675         return PropertyMemberType.DOUBLE_TYPE;
1676     } else if (is(ti == Nullable!byte)) {
1677         return PropertyMemberType.NULLABLE_BYTE_TYPE;
1678     } else if (is(ti == Nullable!short)) {
1679         return PropertyMemberType.NULLABLE_SHORT_TYPE;
1680     } else if (is(ti == Nullable!int)) {
1681         return PropertyMemberType.NULLABLE_INT_TYPE;
1682     } else if (is(ti == Nullable!long)) {
1683         return PropertyMemberType.NULLABLE_LONG_TYPE;
1684     } else if (is(ti == Nullable!ubyte)) {
1685         return PropertyMemberType.NULLABLE_UBYTE_TYPE;
1686     } else if (is(ti == Nullable!ushort)) {
1687         return PropertyMemberType.NULLABLE_USHORT_TYPE;
1688     } else if (is(ti == Nullable!uint)) {
1689         return PropertyMemberType.NULLABLE_UINT_TYPE;
1690     } else if (is(ti == Nullable!ulong)) {
1691         return PropertyMemberType.NULLABLE_ULONG_TYPE;
1692     } else if (is(ti == Nullable!float)) {
1693         return PropertyMemberType.NULLABLE_FLOAT_TYPE;
1694     } else if (is(ti == Nullable!double)) {
1695         return PropertyMemberType.NULLABLE_DOUBLE_TYPE;
1696     } else if (is(ti == string)) {
1697         return PropertyMemberType.STRING_TYPE;
1698     } else if (is(ti == hibernated.type.String)) {
1699         return PropertyMemberType.NULLABLE_STRING_TYPE;
1700     } else if (is(ti == DateTime)) {
1701         return PropertyMemberType.DATETIME_TYPE;
1702     } else if (is(ti == Date)) {
1703         return PropertyMemberType.DATE_TYPE;
1704     } else if (is(ti == TimeOfDay)) {
1705         return PropertyMemberType.TIME_TYPE;
1706     } else if (is(ti == Nullable!DateTime)) {
1707         return PropertyMemberType.NULLABLE_DATETIME_TYPE;
1708     } else if (is(ti == Nullable!Date)) {
1709         return PropertyMemberType.NULLABLE_DATE_TYPE;
1710     } else if (is(ti == Nullable!TimeOfDay)) {
1711         return PropertyMemberType.NULLABLE_TIME_TYPE;
1712     } else if (is(ti == byte[])) {
1713         return PropertyMemberType.BYTE_ARRAY_TYPE;
1714     } else if (is(ti == ubyte[])) {
1715         return PropertyMemberType.UBYTE_ARRAY_TYPE;
1716     } else {
1717         assert (false, "Member " ~ m ~ " of class " ~ T.stringof ~ " has unsupported type " ~ ti.stringof);
1718     }
1719     //static assert (false, "Member " ~ m ~ " of class " ~ T.stringof ~ " has unsupported type " ~ ti.stringof);
1720 }
1721 
1722 
1723 string getPropertyReadCode(T, string m)() {
1724     return substituteParam(PropertyMemberKind_ReadCode[getPropertyMemberKind!(T,m)()], m);
1725 }
1726 
1727 static immutable bool[] ColumnTypeCanHoldNulls = 
1728     [
1729      false, //BOOL_TYPE     // bool
1730      false, //BYTE_TYPE,    // byte
1731      false, //SHORT_TYPE,   // short
1732      false, //INT_TYPE,     // int
1733      false, //LONG_TYPE,    // long
1734      false, //UBYTE_TYPE,   // ubyte
1735      false, //USHORT_TYPE,  // ushort
1736      false, //UINT_TYPE,    // uint
1737      false, //ULONG_TYPE,   // ulong
1738      true, //NULLABLE_BYTE_TYPE,  // Nullable!byte
1739      true, //NULLABLE_SHORT_TYPE, // Nullable!short
1740      true, //NULLABLE_INT_TYPE,   // Nullable!int
1741      true, //NULLABLE_LONG_TYPE,  // Nullable!long
1742      true, //NULLABLE_UBYTE_TYPE, // Nullable!ubyte
1743      true, //NULLABLE_USHORT_TYPE,// Nullable!ushort
1744      true, //NULLABLE_UINT_TYPE,  // Nullable!uint
1745      true, //NULLABLE_ULONG_TYPE, // Nullable!ulong
1746      false,//FLOAT_TYPE,   // float
1747      false,//DOUBLE_TYPE,   // double
1748      true, //NULLABLE_FLOAT_TYPE, // Nullable!float
1749      true, //NULLABLE_DOUBLE_TYPE,// Nullable!double
1750      false, //STRING_TYPE   // string  -- treat as @NotNull by default
1751      true, //NULLABLE_STRING_TYPE   // String
1752      false, //DATETIME_TYPE, // std.datetime.DateTime
1753      false, //DATE_TYPE, // std.datetime.Date
1754      false, //TIME_TYPE, // std.datetime.TimeOfDay
1755      true, //NULLABLE_DATETIME_TYPE, // Nullable!std.datetime.DateTime
1756      true, //NULLABLE_DATE_TYPE, // Nullable!std.datetime.Date
1757      true, //NULLABLE_TIME_TYPE, // Nullable!std.datetime.TimeOfDay
1758      true, //BYTE_ARRAY_TYPE, // byte[]
1759      true, //UBYTE_ARRAY_TYPE, // ubyte[]
1760      ];
1761 
1762 bool isColumnTypeNullableByDefault(T, string m)() {
1763     return ColumnTypeCanHoldNulls[getPropertyMemberType!(T,m)];
1764 }
1765 
1766 
1767 static immutable string[] ColumnTypeKeyIsSetCode = 
1768     [
1769      "(%s != 0)", //BOOL_TYPE     // bool
1770      "(%s != 0)", //BYTE_TYPE,    // byte
1771      "(%s != 0)", //SHORT_TYPE,   // short
1772      "(%s != 0)", //INT_TYPE,     // int
1773      "(%s != 0)", //LONG_TYPE,    // long
1774      "(%s != 0)", //UBYTE_TYPE,   // ubyte
1775      "(%s != 0)", //USHORT_TYPE,  // ushort
1776      "(%s != 0)", //UINT_TYPE,    // uint
1777      "(%s != 0)", //ULONG_TYPE,   // ulong
1778      "(!%s.isNull)", //NULLABLE_BYTE_TYPE,  // Nullable!byte
1779      "(!%s.isNull)", //NULLABLE_SHORT_TYPE, // Nullable!short
1780      "(!%s.isNull)", //NULLABLE_INT_TYPE,   // Nullable!int
1781      "(!%s.isNull)", //NULLABLE_LONG_TYPE,  // Nullable!long
1782      "(!%s.isNull)", //NULLABLE_UBYTE_TYPE, // Nullable!ubyte
1783      "(!%s.isNull)", //NULLABLE_USHORT_TYPE,// Nullable!ushort
1784      "(!%s.isNull)", //NULLABLE_UINT_TYPE,  // Nullable!uint
1785      "(!%s.isNull)", //NULLABLE_ULONG_TYPE, // Nullable!ulong
1786      "(%s != 0)",//FLOAT_TYPE,   // float
1787      "(%s != 0)",//DOUBLE_TYPE,   // double
1788      "(!%s.isNull)", //NULLABLE_FLOAT_TYPE, // Nullable!float
1789      "(!%s.isNull)", //NULLABLE_DOUBLE_TYPE,// Nullable!double
1790      "(%s !is null)", //STRING_TYPE   // string
1791      "(%s !is null)", //NULLABLE_STRING_TYPE   // String
1792      "(%s != DateTime())", //DATETIME_TYPE, // std.datetime.DateTime
1793      "(%s != Date())", //DATE_TYPE, // std.datetime.Date
1794      "(%s != TimeOfDay())", //TIME_TYPE, // std.datetime.TimeOfDay
1795      "(!%s.isNull)", //NULLABLE_DATETIME_TYPE, // Nullable!std.datetime.DateTime
1796      "(!%s.isNull)", //NULLABLE_DATE_TYPE, // Nullable!std.datetime.Date
1797      "(!%s.isNull)", //NULLABLE_TIME_TYPE, // Nullable!std.datetime.TimeOfDay
1798      "(%s !is null)", //BYTE_ARRAY_TYPE, // byte[]
1799      "(%s !is null)", //UBYTE_ARRAY_TYPE, // ubyte[]
1800      ];
1801 
1802 string getColumnTypeKeyIsSetCode(T, string m)() {
1803     return substituteParam(ColumnTypeKeyIsSetCode[getPropertyMemberType!(T,m)()], getPropertyReadCode!(T,m)());
1804 }
1805 
1806 static immutable string[] ColumnTypeIsNullCode = 
1807     [
1808      "(false)", //BOOL_TYPE     // bool
1809      "(false)", //BYTE_TYPE,    // byte
1810      "(false)", //SHORT_TYPE,   // short
1811      "(false)", //INT_TYPE,     // int
1812      "(false)", //LONG_TYPE,    // long
1813      "(false)", //UBYTE_TYPE,   // ubyte
1814      "(false)", //USHORT_TYPE,  // ushort
1815      "(false)", //UINT_TYPE,    // uint
1816      "(false)", //ULONG_TYPE,   // ulong
1817      "(%s.isNull)", //NULLABLE_BYTE_TYPE,  // Nullable!byte
1818      "(%s.isNull)", //NULLABLE_SHORT_TYPE, // Nullable!short
1819      "(%s.isNull)", //NULLABLE_INT_TYPE,   // Nullable!int
1820      "(%s.isNull)", //NULLABLE_LONG_TYPE,  // Nullable!long
1821      "(%s.isNull)", //NULLABLE_UBYTE_TYPE, // Nullable!ubyte
1822      "(%s.isNull)", //NULLABLE_USHORT_TYPE,// Nullable!ushort
1823      "(%s.isNull)", //NULLABLE_UINT_TYPE,  // Nullable!uint
1824      "(%s.isNull)", //NULLABLE_ULONG_TYPE, // Nullable!ulong
1825      "(false)",//FLOAT_TYPE,   // float
1826      "(false)",//DOUBLE_TYPE,   // double
1827      "(%s.isNull)", //NULLABLE_FLOAT_TYPE, // Nullable!float
1828      "(%s.isNull)", //NULLABLE_DOUBLE_TYPE,// Nullable!double
1829      "(%s is null)", //STRING_TYPE   // string
1830      "(%s is null)", //NULLABLE_STRING_TYPE   // String
1831      "(false)", //DATETIME_TYPE, // std.datetime.DateTime
1832      "(false)", //DATE_TYPE, // std.datetime.Date
1833      "(false)", //TIME_TYPE, // std.datetime.TimeOfDay
1834      "(%s.isNull)", //NULLABLE_DATETIME_TYPE, // Nullable!std.datetime.DateTime
1835      "(%s.isNull)", //NULLABLE_DATE_TYPE, // Nullable!std.datetime.Date
1836      "(%s.isNull)", //NULLABLE_TIME_TYPE, // Nullable!std.datetime.TimeOfDay
1837      "(%s is null)", //BYTE_ARRAY_TYPE, // byte[]
1838      "(%s is null)", //UBYTE_ARRAY_TYPE, // ubyte[]
1839      ];
1840 
1841 string getColumnTypeIsNullCode(T, string m)() {
1842     return substituteParam(ColumnTypeIsNullCode[getPropertyMemberType!(T,m)()], getPropertyReadCode!(T,m)());
1843 }
1844 
1845 static immutable string[] ColumnTypeSetNullCode = 
1846     [
1847      "bool nv;", // BOOL_TYPE   // bool
1848      "byte nv = 0;", //BYTE_TYPE,    // byte
1849      "short nv = 0;", //SHORT_TYPE,   // short
1850      "int nv = 0;", //INT_TYPE,     // int
1851      "long nv = 0;", //LONG_TYPE,    // long
1852      "ubyte nv = 0;", //UBYTE_TYPE,   // ubyte
1853      "ushort nv = 0;", //USHORT_TYPE,  // ushort
1854      "uint nv = 0;", //UINT_TYPE,    // uint
1855      "ulong nv = 0;", //ULONG_TYPE,   // ulong
1856      "Nullable!byte nv;", //NULLABLE_BYTE_TYPE,  // Nullable!byte
1857      "Nullable!short nv;", //NULLABLE_SHORT_TYPE, // Nullable!short
1858      "Nullable!int nv;", //NULLABLE_INT_TYPE,   // Nullable!int
1859      "Nullable!long nv;", //NULLABLE_LONG_TYPE,  // Nullable!long
1860      "Nullable!ubyte nv;", //NULLABLE_UBYTE_TYPE, // Nullable!ubyte
1861      "Nullable!ushort nv;", //NULLABLE_USHORT_TYPE,// Nullable!ushort
1862      "Nullable!uint nv;", //NULLABLE_UINT_TYPE,  // Nullable!uint
1863      "Nullable!ulong nv;", //NULLABLE_ULONG_TYPE, // Nullable!ulong
1864      "float nv = 0;",//FLOAT_TYPE,   // float
1865      "double nv = 0;",//DOUBLE_TYPE,   // double
1866      "Nullable!float nv;", //NULLABLE_FLOAT_TYPE, // Nullable!float
1867      "Nullable!double nv;", //NULLABLE_DOUBLE_TYPE,// Nullable!double
1868      "string nv;", //STRING_TYPE   // string
1869      "string nv;", //NULLABLE_STRING_TYPE   // String
1870      "DateTime nv;", //DATETIME_TYPE, // std.datetime.DateTime
1871      "Date nv;", //DATE_TYPE, // std.datetime.Date
1872      "TimeOfDay nv;", //TIME_TYPE, // std.datetime.TimeOfDay
1873      "Nullable!DateTime nv;", //NULLABLE_DATETIME_TYPE, // Nullable!std.datetime.DateTime
1874      "Nullable!Date nv;", //NULLABLE_DATE_TYPE, // Nullable!std.datetime.Date
1875      "Nullable!TimeOfDay nv;", //NULLABLE_TIME_TYPE, // Nullable!std.datetime.TimeOfDay
1876      "byte[] nv = null;", //BYTE_ARRAY_TYPE, // byte[]
1877      "ubyte[] nv = null;", //UBYTE_ARRAY_TYPE, // ubyte[]
1878      ];
1879 
1880 /*
1881  * Due to to change in dmd [issue #72] "Implicit conversion with alias Nullable.get this will be removed after 2.096", nullable types (except string) have to make call to .get
1882 */
1883 static immutable string[] ColumnTypePropertyToVariant = 
1884     [
1885      "Variant(%s)", //BOOL_TYPE     // bool
1886      "Variant(%s)", //BYTE_TYPE,    // byte
1887      "Variant(%s)", //SHORT_TYPE,   // short
1888      "Variant(%s)", //INT_TYPE,     // int
1889      "Variant(%s)", //LONG_TYPE,    // long
1890      "Variant(%s)", //UBYTE_TYPE,   // ubyte
1891      "Variant(%s)", //USHORT_TYPE,  // ushort
1892      "Variant(%s)", //UINT_TYPE,    // uint
1893      "Variant(%s)", //ULONG_TYPE,   // ulong
1894      "(%s.isNull ? Variant(null) : Variant(%s.get))", //NULLABLE_BYTE_TYPE,  // Nullable!byte
1895      "(%s.isNull ? Variant(null) : Variant(%s.get))", //NULLABLE_SHORT_TYPE, // Nullable!short
1896      "(%s.isNull ? Variant(null) : Variant(%s.get))", //NULLABLE_INT_TYPE,   // Nullable!int
1897      "(%s.isNull ? Variant(null) : Variant(%s.get))", //NULLABLE_LONG_TYPE,  // Nullable!long
1898      "(%s.isNull ? Variant(null) : Variant(%s.get))", //NULLABLE_UBYTE_TYPE, // Nullable!ubyte
1899      "(%s.isNull ? Variant(null) : Variant(%s.get))", //NULLABLE_USHORT_TYPE,// Nullable!ushort
1900      "(%s.isNull ? Variant(null) : Variant(%s.get))", //NULLABLE_UINT_TYPE,  // Nullable!uint
1901      "(%s.isNull ? Variant(null) : Variant(%s.get))", //NULLABLE_ULONG_TYPE, // Nullable!ulong
1902      "Variant(%s)",//FLOAT_TYPE,   // float
1903      "Variant(%s)",//DOUBLE_TYPE,   // double
1904      "(%s.isNull ? Variant(null) : Variant(%s.get))", //NULLABLE_FLOAT_TYPE, // Nullable!float
1905      "(%s.isNull ? Variant(null) : Variant(%s.get))", //NULLABLE_DOUBLE_TYPE,// Nullable!double
1906      "Variant(%s)", //STRING_TYPE   // string
1907      "Variant(%s)", //NULLABLE_STRING_TYPE   // String
1908      "Variant(%s)", //DATETIME_TYPE, // std.datetime.DateTime
1909      "Variant(%s)", //DATE_TYPE, // std.datetime.Date
1910      "Variant(%s)", //TIME_TYPE, // std.datetime.TimeOfDay
1911      "(%s.isNull ? Variant(null) : Variant(%s.get))", //NULLABLE_DATETIME_TYPE, // Nullable!std.datetime.DateTime
1912      "(%s.isNull ? Variant(null) : Variant(%s.get))", //NULLABLE_DATE_TYPE, // Nullable!std.datetime.Date
1913      "(%s.isNull ? Variant(null) : Variant(%s.get))", //NULLABLE_TIME_TYPE, // Nullable!std.datetime.TimeOfDay
1914      "Variant(%s)", //BYTE_ARRAY_TYPE, // byte[]
1915      "Variant(%s)", //UBYTE_ARRAY_TYPE, // ubyte[]
1916      ];
1917 
1918 string getPropertyWriteCode(T, string m)() {
1919     immutable PropertyMemberKind kind = getPropertyMemberKind!(T, m)();
1920     immutable string nullValueCode = ColumnTypeSetNullCode[getPropertyMemberType!(T,m)()];
1921     immutable string datasetReader = "(!r.isNull(index) ? " ~ getColumnTypeDatasetReadCode!(T, m)() ~ " : nv)";
1922 
1923     final switch (kind) {
1924         case PropertyMemberKind.FIELD_MEMBER:
1925             return nullValueCode ~ "entity." ~ m ~ " = " ~ datasetReader ~ ";";
1926         case PropertyMemberKind.LAZY_MEMBER:
1927             return nullValueCode ~ "entity." ~ m ~ " = " ~ datasetReader ~ ";";
1928         case PropertyMemberKind.GETTER_MEMBER:
1929             return nullValueCode ~ "entity." ~ getterNameToSetterName(m) ~ "(" ~ datasetReader ~ ");";
1930         case PropertyMemberKind.PROPERTY_MEMBER:
1931             return nullValueCode ~ "entity." ~ m ~ " = " ~ datasetReader ~ ";";
1932         case PropertyMemberKind.UNSUPPORTED_MEMBER:
1933             assert(false, "Unsupported member kind " ~ T.stringof ~ "." ~ m ~ " " ~ typeof(__traits(getMember, T, m)).stringof);
1934     }
1935 }
1936 
1937 string getPropertyCopyCode(T, string m)() {
1938     immutable PropertyMemberKind kind = getPropertyMemberKind!(T, m)();
1939 
1940     final switch (kind) {
1941         case PropertyMemberKind.FIELD_MEMBER:
1942             return "toentity." ~ m ~ " = fromentity." ~ m ~ ";";
1943         case PropertyMemberKind.LAZY_MEMBER:
1944             return "toentity." ~ m ~ " = fromentity." ~ m ~ "();";
1945         case PropertyMemberKind.GETTER_MEMBER:
1946             return "toentity." ~ getterNameToSetterName(m) ~ "(fromentity." ~ m ~ "());";
1947         case PropertyMemberKind.PROPERTY_MEMBER:
1948             return "toentity." ~ m ~ " = fromentity." ~ m ~ ";";
1949         case PropertyMemberKind.UNSUPPORTED_MEMBER:
1950             assert(false, "Unsupported member kind " ~ T.stringof ~ "." ~ m);
1951     }
1952 }
1953 
1954 string getPropertyVariantWriteCode(T, string m)() {
1955     immutable memberType = getPropertyMemberType!(T,m)();
1956     immutable string nullValueCode = ColumnTypeSetNullCode[memberType];
1957     immutable string variantReadCode = ColumnTypeVariantReadCode[memberType];
1958 
1959     static if (getPropertyMemberKind!(T, m)() == PropertyMemberKind.GETTER_MEMBER) {
1960         return nullValueCode ~ "entity." ~ getterNameToSetterName(m) ~ "(" ~ variantReadCode ~ ");";
1961     } else {
1962         return nullValueCode ~ "entity." ~ m ~ " = " ~ variantReadCode ~ ";";
1963     }
1964 }
1965 
1966 string getPropertyVariantReadCode(T, string m)() {
1967     immutable memberType = getPropertyMemberType!(T,m)();
1968     immutable string propertyReadCode = getPropertyReadCode!(T,m)();
1969     return substituteParamTwice(ColumnTypePropertyToVariant[memberType], propertyReadCode);
1970 }
1971 
1972 
1973 static immutable string[] ColumnTypeConstructorCode = 
1974     [
1975      "new BooleanType()", // BOOL_TYPE, bool
1976      "new NumberType(2, false, SqlType.TINYINT)", //BYTE_TYPE,    // byte
1977      "new NumberType(4, false, SqlType.SMALLINT)", //SHORT_TYPE,   // short
1978      "new NumberType(9, false, SqlType.INTEGER)", //INT_TYPE,     // int
1979      "new NumberType(20, false, SqlType.BIGINT)", //LONG_TYPE,    // long
1980      "new NumberType(2, true, SqlType.TINYINT)", //UBYTE_TYPE,   // ubyte
1981      "new NumberType(4, true, SqlType.SMALLINT)", //USHORT_TYPE,  // ushort
1982      "new NumberType(9, true, SqlType.INTEGER)", //UINT_TYPE,    // uint
1983      "new NumberType(20, true, SqlType.BIGINT)", //ULONG_TYPE,   // ulong
1984      "new NumberType(2, false, SqlType.TINYINT)", //NULLABLE_BYTE_TYPE,  // Nullable!byte
1985      "new NumberType(4, false, SqlType.SMALLINT)", //NULLABLE_SHORT_TYPE, // Nullable!short
1986      "new NumberType(9, false, SqlType.INTEGER)", //NULLABLE_INT_TYPE,   // Nullable!int
1987      "new NumberType(20, false, SqlType.BIGINT)", //NULLABLE_LONG_TYPE,  // Nullable!long
1988      "new NumberType(2, true, SqlType.TINYINT)", //NULLABLE_UBYTE_TYPE, // Nullable!ubyte
1989      "new NumberType(4, true, SqlType.SMALLINT)", //NULLABLE_USHORT_TYPE,// Nullable!ushort
1990      "new NumberType(9, true, SqlType.INTEGER)", //NULLABLE_UINT_TYPE,  // Nullable!uint
1991      "new NumberType(20, true, SqlType.BIGINT)", //NULLABLE_ULONG_TYPE, // Nullable!ulong
1992      "new NumberType(7, false, SqlType.FLOAT)",//FLOAT_TYPE,   // float
1993      "new NumberType(14, false, SqlType.DOUBLE)",//DOUBLE_TYPE,   // double
1994      "new NumberType(7, false, SqlType.FLOAT)", //NULLABLE_FLOAT_TYPE, // Nullable!float
1995      "new NumberType(14, false, SqlType.DOUBLE)", //NULLABLE_DOUBLE_TYPE,// Nullable!double
1996      "new StringType()", //STRING_TYPE   // string
1997      "new StringType()", //NULLABLE_STRING_TYPE   // String
1998      "new DateTimeType()", //DATETIME_TYPE, // std.datetime.DateTime
1999      "new DateType()", //DATE_TYPE, // std.datetime.Date
2000      "new TimeType()", //TIME_TYPE, // std.datetime.TimeOfDay
2001      "new DateTimeType()", //NULLABLE_DATETIME_TYPE, // Nullable!std.datetime.DateTime
2002      "new DateType()", //NULLABLE_DATE_TYPE, // Nullable!std.datetime.Date
2003      "new TimeType()", //NULLABLE_TIME_TYPE, // Nullable!std.datetime.TimeOfDay
2004      "new ByteArrayBlobType()", //BYTE_ARRAY_TYPE, // byte[]
2005      "new UbyteArrayBlobType()", //UBYTE_ARRAY_TYPE, // ubyte[]
2006      ];
2007 
2008 string getColumnTypeName(T, string m, int length)() {
2009     immutable PropertyMemberType mt = getPropertyMemberType!(T,m);
2010     static if (mt == PropertyMemberType.STRING_TYPE || mt == PropertyMemberType.NULLABLE_STRING_TYPE) {
2011         return "new StringType(" ~ to!string(length) ~ ")";
2012     } else {
2013         return ColumnTypeConstructorCode[mt];
2014     }
2015 }
2016 
2017 static immutable string[] ColumnTypeDatasetReaderCode = 
2018     [
2019      "r.getBoolean(index)", //BOOL_TYPE,    // bool
2020      "r.getByte(index)", //BYTE_TYPE,    // byte
2021      "r.getShort(index)", //SHORT_TYPE,   // short
2022      "r.getInt(index)", //INT_TYPE,     // int
2023      "r.getLong(index)", //LONG_TYPE,    // long
2024      "r.getUbyte(index)", //UBYTE_TYPE,   // ubyte
2025      "r.getUshort(index)", //USHORT_TYPE,  // ushort
2026      "r.getUint(index)", //UINT_TYPE,    // uint
2027      "r.getUlong(index)", //ULONG_TYPE,   // ulong
2028      "Nullable!byte(r.getByte(index))", //NULLABLE_BYTE_TYPE,  // Nullable!byte
2029      "Nullable!short(r.getShort(index))", //NULLABLE_SHORT_TYPE, // Nullable!short
2030      "Nullable!int(r.getInt(index))", //NULLABLE_INT_TYPE,   // Nullable!int
2031      "Nullable!long(r.getLong(index))", //NULLABLE_LONG_TYPE,  // Nullable!long
2032      "Nullable!ubyte(r.getUbyte(index))", //NULLABLE_UBYTE_TYPE, // Nullable!ubyte
2033      "Nullable!ushort(r.getUshort(index))", //NULLABLE_USHORT_TYPE,// Nullable!ushort
2034      "Nullable!uint(r.getUint(index))", //NULLABLE_UINT_TYPE,  // Nullable!uint
2035      "Nullable!ulong(r.getUlong(index))", //NULLABLE_ULONG_TYPE, // Nullable!ulong
2036      "r.getFloat(index)",//FLOAT_TYPE,   // float
2037      "r.getDouble(index)",//DOUBLE_TYPE,   // double
2038      "Nullable!float(r.getFloat(index))", //NULLABLE_FLOAT_TYPE, // Nullable!float
2039      "Nullable!double(r.getDouble(index))", //NULLABLE_DOUBLE_TYPE,// Nullable!double
2040      "r.getString(index)", //STRING_TYPE   // string
2041      "r.getString(index)", //NULLABLE_STRING_TYPE   // String
2042      "r.getDateTime(index)", //DATETIME_TYPE, // std.datetime.DateTime
2043      "r.getDate(index)", //DATE_TYPE, // std.datetime.Date
2044      "r.getTime(index)", //TIME_TYPE, // std.datetime.TimeOfDay
2045      "Nullable!DateTime(r.getDateTime(index))", //NULLABLE_DATETIME_TYPE, // Nullable!std.datetime.DateTime
2046      "Nullable!Date(r.getDate(index))", //NULLABLE_DATE_TYPE, // Nullable!std.datetime.Date
2047      "Nullable!TimeOfDay(r.getTime(index))", //NULLABLE_TIME_TYPE, // Nullable!std.datetime.TimeOfDay
2048      "r.getBytes(index)", //BYTE_ARRAY_TYPE, // byte[]
2049      "r.getUbytes(index)", //UBYTE_ARRAY_TYPE, // ubyte[]
2050      ];
2051 
2052 string getColumnTypeDatasetReadCode(T, string m)() {
2053     return ColumnTypeDatasetReaderCode[getPropertyMemberType!(T,m)()];
2054 }
2055 
2056 /*
2057  * Due to to change in dmd [issue #72] "Implicit conversion with alias Nullable.get this will be removed after 2.096", nullable types (except string) have to make call to .get
2058 */
2059 static immutable string[] ColumnTypeVariantReadCode = 
2060     [
2061      "(value == null ? nv : value.get!(bool))", //BOOL_TYPE,    // bool
2062      "(value == null ? nv : (value.convertsTo!(byte) ? value.get!(byte) : (value.convertsTo!(long) ? to!byte(value.get!(long)) : to!byte((value.get!(ulong))))))", //BYTE_TYPE,    // byte
2063      "(value == null ? nv : (value.convertsTo!(short) ? value.get!(short) : (value.convertsTo!(long) ? to!short(value.get!(long)) : to!short((value.get!(ulong))))))", //SHORT_TYPE,   // short
2064      "(value == null ? nv : (value.convertsTo!(int) ? value.get!(int) : (value.convertsTo!(long) ? to!int(value.get!(long)) : to!int((value.get!(ulong))))))", //INT_TYPE,     // int
2065      "(value == null ? nv : (value.convertsTo!(long) ? value.get!(long) : to!long(value.get!(ulong))))", //LONG_TYPE,    // long
2066      "(value == null ? nv : (value.convertsTo!(ubyte) ? value.get!(ubyte) : (value.convertsTo!(ulong) ? to!ubyte(value.get!(ulong)) : to!ubyte((value.get!(long))))))", //UBYTE_TYPE,   // ubyte
2067      "(value == null ? nv : (value.convertsTo!(ushort) ? value.get!(ushort) : (value.convertsTo!(ulong) ? to!ushort(value.get!(ulong)) : to!ushort((value.get!(long))))))", //USHORT_TYPE,  // ushort
2068      "(value == null ? nv : (value.convertsTo!(uint) ? value.get!(uint) : (value.convertsTo!(ulong) ? to!uint(value.get!(ulong)) : to!uint((value.get!(long))))))", //UINT_TYPE,    // uint
2069      "(value == null ? nv : (value.convertsTo!(ulong) ? value.get!(ulong) : to!ulong(value.get!(long))))", //ULONG_TYPE,   // ulong
2070      "(value == null ? nv.get : (value.convertsTo!(byte) ? value.get!(byte) : (value.convertsTo!(long) ? to!byte(value.get!(long)) : to!byte((value.get!(ulong))))))", //NULLABLE_BYTE_TYPE,  // Nullable!byte
2071      "(value == null ? nv.get : (value.convertsTo!(short) ? value.get!(short) : (value.convertsTo!(long) ? to!short(value.get!(long)) : to!short((value.get!(ulong))))))", //NULLABLE_SHORT_TYPE, // Nullable!short
2072      "(value == null ? nv.get : (value.convertsTo!(int) ? value.get!(int) : (value.convertsTo!(long) ? to!int(value.get!(long)) : to!int((value.get!(ulong))))))", //NULLABLE_INT_TYPE,   // Nullable!int
2073      "(value == null ? nv.get : (value.convertsTo!(long) ? value.get!(long) : to!long(value.get!(ulong))))", //NULLABLE_LONG_TYPE,  // Nullable!long
2074      "(value == null ? nv.get : (value.convertsTo!(ubyte) ? value.get!(ubyte) : (value.convertsTo!(ulong) ? to!ubyte(value.get!(ulong)) : to!ubyte((value.get!(long))))))", //NULLABLE_UBYTE_TYPE, // Nullable!ubyte
2075      "(value == null ? nv.get : (value.convertsTo!(ushort) ? value.get!(ushort) : (value.convertsTo!(ulong) ? to!ushort(value.get!(ulong)) : to!ushort((value.get!(long))))))", //NULLABLE_USHORT_TYPE,// Nullable!ushort
2076      "(value == null ? nv.get : (value.convertsTo!(uint) ? value.get!(uint) : (value.convertsTo!(ulong) ? to!uint(value.get!(ulong)) : to!uint((value.get!(long))))))", //NULLABLE_UINT_TYPE,  // Nullable!uint
2077      "(value == null ? nv.get : (value.convertsTo!(ulong) ? value.get!(ulong) : to!ulong(value.get!(long))))", //NULLABLE_ULONG_TYPE, // Nullable!ulong
2078      "(value == null ? nv : (value.convertsTo!(float) ? value.get!(float) : to!float(value.get!(double))))",//FLOAT_TYPE,   // float
2079      "(value == null ? nv : (value.convertsTo!(double) ? value.get!(double) : to!double(value.get!(double))))",//DOUBLE_TYPE,   // double
2080      "(value == null ? nv.get : (value.convertsTo!(float) ? value.get!(float) : to!float(value.get!(double))))", //NULLABLE_FLOAT_TYPE, // Nullable!float
2081      "(value == null ? nv.get : (value.convertsTo!(double) ? value.get!(double) : to!double(value.get!(double))))", //NULLABLE_DOUBLE_TYPE,// Nullable!double
2082      "(value == null ? nv : value.get!(string))", //STRING_TYPE   // string
2083      "(value == null ? nv : value.get!(string))", //NULLABLE_STRING_TYPE   // String
2084      "(value == null ? nv : value.get!(DateTime))", //DATETIME_TYPE, // std.datetime.DateTime
2085      "(value == null ? nv : value.get!(Date))", //DATE_TYPE, // std.datetime.Date
2086      "(value == null ? nv : value.get!(TimeOfDay))", //TIME_TYPE, // std.datetime.TimeOfDay
2087      "(value == null ? nv.get : value.get!(DateTime))", //NULLABLE_DATETIME_TYPE, // Nullable!std.datetime.DateTime
2088      "(value == null ? nv.get : value.get!(Date))", //NULLABLE_DATE_TYPE, // Nullable!std.datetime.Date
2089      "(value == null ? nv.get : value.get!(TimeOfDay))", //NULLABLE_TIME_TYPE, // Nullable!std.datetime.TimeOfDay
2090      "(value == null ? nv : value.get!(byte[]))", //BYTE_ARRAY_TYPE, // byte[]
2091      "(value == null ? nv : value.get!(ubyte[]))", //UBYTE_ARRAY_TYPE, // ubyte[]
2092      ];
2093 
2094 /*
2095  * Due to to change in dmd [issue #72] "Implicit conversion with alias Nullable.get this will be removed after 2.096", nullable types (except string) have to make call to .get
2096 */
2097 static immutable string[] DatasetWriteCode = 
2098     [
2099      "r.setBoolean(index, %s);", //BOOL_TYPE,    // bool
2100      "r.setByte(index, %s);", //BYTE_TYPE,    // byte
2101      "r.setShort(index, %s);", //SHORT_TYPE,   // short
2102      "r.setInt(index, %s);", //INT_TYPE,     // int
2103      "r.setLong(index, %s);", //LONG_TYPE,    // long
2104      "r.setUbyte(index, %s);", //UBYTE_TYPE,   // ubyte
2105      "r.setUshort(index, %s);", //USHORT_TYPE,  // ushort
2106      "r.setUint(index, %s);", //UINT_TYPE,    // uint
2107      "r.setUlong(index, %s);", //ULONG_TYPE,   // ulong
2108      "r.setByte(index, %s.get);", //NULLABLE_BYTE_TYPE,  // Nullable!byte
2109      "r.setShort(index, %s.get);", //NULLABLE_SHORT_TYPE, // Nullable!short
2110      "r.setInt(index, %s.get);", //NULLABLE_INT_TYPE,   // Nullable!int
2111      "r.setLong(index, %s.get);", //NULLABLE_LONG_TYPE,  // Nullable!long
2112      "r.setUbyte(index, %s.get);", //NULLABLE_UBYTE_TYPE, // Nullable!ubyte
2113      "r.setUshort(index, %s.get);", //NULLABLE_USHORT_TYPE,// Nullable!ushort
2114      "r.setUint(index, %s.get);", //NULLABLE_UINT_TYPE,  // Nullable!uint
2115      "r.setUlong(index, %s.get);", //NULLABLE_ULONG_TYPE, // Nullable!ulong
2116      "r.setFloat(index, %s);",//FLOAT_TYPE,   // float
2117      "r.setDouble(index, %s);",//DOUBLE_TYPE,   // double
2118      "r.setFloat(index, %s.get);", //NULLABLE_FLOAT_TYPE, // Nullable!float
2119      "r.setDouble(index, %s.get);", //NULLABLE_DOUBLE_TYPE,// Nullable!double
2120      "r.setString(index, %s);", //STRING_TYPE   // string
2121      "r.setString(index, %s);", //NULLABLE_STRING_TYPE   // String (don't need to call .get on this one as string is already nullable)
2122      "r.setDateTime(index, %s);", //DATETIME_TYPE, // std.datetime.DateTime
2123      "r.setDate(index, %s);", //DATE_TYPE, // std.datetime.Date
2124      "r.setTime(index, %s);", //TIME_TYPE, // std.datetime.TimeOfDay
2125      "r.setDateTime(index, %s.get);", //NULLABLE_DATETIME_TYPE, // Nullable!std.datetime.DateTime
2126      "r.setDate(index, %s.get);", //NULLABLE_DATE_TYPE, // Nullable!std.datetime.Date
2127      "r.setTime(index, %s.get);", //NULLABLE_TIME_TYPE, // Nullable!std.datetime.TimeOfDay
2128      "r.setBytes(index, %s);", //BYTE_ARRAY_TYPE, // byte[]
2129      "r.setUbytes(index, %s);", //UBYTE_ARRAY_TYPE, // ubyte[]
2130      ];
2131 
2132 string getColumnTypeDatasetWriteCode(T, string m)() {
2133     alias ti = typeof(__traits(getMember, T, m));
2134 
2135     immutable string isNullCode = getColumnTypeIsNullCode!(T,m)();
2136     immutable string readCode = getPropertyReadCode!(T,m)();
2137     immutable string setDataCode = DatasetWriteCode[getPropertyMemberType!(T,m)()];
2138     return "if (" ~ isNullCode ~ ") r.setNull(index); else " ~ substituteParam(setDataCode, readCode);
2139 }
2140 
2141 string getEmbeddedPropertyVariantWriteCode(T, string m, string className)() {
2142     immutable PropertyMemberKind kind = getPropertyMemberKind!(T, m)();
2143     final switch (kind) {
2144         case PropertyMemberKind.FIELD_MEMBER:
2145             return "entity." ~ m ~ " = (value == null ? null : value.get!(" ~ className ~ "));";
2146         case PropertyMemberKind.GETTER_MEMBER:
2147             return "entity." ~ getterNameToSetterName(m) ~ "(value == null ? null : value.get!(" ~ className ~ "));";
2148         case PropertyMemberKind.LAZY_MEMBER:
2149             return "entity." ~ m ~ " = (value == null ? null : value.get!(" ~ className ~ "));";
2150         case PropertyMemberKind.PROPERTY_MEMBER:
2151             return "entity." ~ m ~ " = (value == null ? null : value.get!(" ~ className ~ "));";
2152         case PropertyMemberKind.UNSUPPORTED_MEMBER:
2153             assert(false, "Unsupported member kind " ~ T.stringof ~ "." ~ m);
2154     }
2155 }
2156 
2157 string getCollectionPropertyVariantWriteCode(T, string m, string className)() {
2158     immutable PropertyMemberKind kind = getPropertyMemberKind!(T, m)();
2159     final switch (kind) {
2160         case PropertyMemberKind.FIELD_MEMBER:
2161             return "entity." ~ m ~ " = (value == null ? null : value.get!(" ~ className ~ "[]));";
2162         case PropertyMemberKind.GETTER_MEMBER:
2163             return "entity." ~ getterNameToSetterName(m) ~ "(value == null ? null : value.get!(" ~ className ~ "[]));";
2164         case PropertyMemberKind.LAZY_MEMBER:
2165             return "entity." ~ m ~ " = (value == null ? null : value.get!(" ~ className ~ "[]));";
2166         case PropertyMemberKind.PROPERTY_MEMBER:
2167             return "entity." ~ m ~ " = (value == null ? null : value.get!(" ~ className ~ "[]));";
2168         case PropertyMemberKind.UNSUPPORTED_MEMBER:
2169             assert(false, "Unsupported member kind " ~ T.stringof ~ "." ~ m);
2170     }
2171 }
2172 
2173 string getPropertyObjectWriteCode(T, string m, string className)() {
2174     immutable PropertyMemberKind kind = getPropertyMemberKind!(T, m)();
2175     final switch (kind) {
2176         case PropertyMemberKind.FIELD_MEMBER:
2177             return "entity." ~ m ~ " = cast(" ~ className ~ ")value;";
2178         case PropertyMemberKind.GETTER_MEMBER:
2179             return "entity." ~ getterNameToSetterName(m) ~ "(cast(" ~ className ~ ")value);";
2180         case PropertyMemberKind.PROPERTY_MEMBER:
2181             return "entity." ~ m ~ " = cast(" ~ className ~ ")value;";
2182         case PropertyMemberKind.LAZY_MEMBER:
2183             return "entity." ~ m ~ " = cast(" ~ className ~ ")value;";
2184         case PropertyMemberKind.UNSUPPORTED_MEMBER:
2185             assert(false, "Unsupported member kind " ~ T.stringof ~ "." ~ m);
2186     }
2187 }
2188 
2189 string getPropertyCollectionWriteCode(T, string m, string className)() {
2190     immutable PropertyMemberKind kind = getPropertyMemberKind!(T, m)();
2191     final switch (kind) {
2192         case PropertyMemberKind.FIELD_MEMBER:
2193             return "entity." ~ m ~ " = cast(" ~ className ~ "[])value;";
2194         case PropertyMemberKind.GETTER_MEMBER:
2195             return "entity." ~ getterNameToSetterName(m) ~ "(cast(" ~ className ~ "[])value);";
2196         case PropertyMemberKind.PROPERTY_MEMBER:
2197             return "entity." ~ m ~ " = cast(" ~ className ~ "[])value;";
2198         case PropertyMemberKind.LAZY_MEMBER:
2199             return "entity." ~ m ~ " = cast(" ~ className ~ "[])value;";
2200         case PropertyMemberKind.UNSUPPORTED_MEMBER:
2201             assert(false, "Unsupported member kind " ~ T.stringof ~ "." ~ m);
2202     }
2203 }
2204 
2205 string getLazyPropertyObjectWriteCode(T, string m)() {
2206     immutable PropertyMemberKind kind = getPropertyMemberKind!(T, m)();
2207     final switch (kind) {
2208         case PropertyMemberKind.FIELD_MEMBER:
2209             return "entity." ~ m ~ " = loader;";
2210         case PropertyMemberKind.GETTER_MEMBER:
2211             return "entity." ~ getterNameToSetterName(m) ~ "(loader);";
2212         case PropertyMemberKind.PROPERTY_MEMBER:
2213             return "entity." ~ m ~ " = loader;";
2214         case PropertyMemberKind.LAZY_MEMBER:
2215             return "entity." ~ m ~ " = loader;";
2216         case PropertyMemberKind.UNSUPPORTED_MEMBER:
2217             assert(false, "Unsupported member kind " ~ T.stringof ~ "." ~ m);
2218     }
2219 }
2220 
2221 string getLazyPropertyLoadedCode(T, string m)() {
2222     immutable PropertyMemberKind kind = getPropertyMemberKind!(T, m)();
2223     final switch (kind) {
2224         case PropertyMemberKind.FIELD_MEMBER:
2225             return "entity." ~ m ~ ".loaded";
2226         case PropertyMemberKind.GETTER_MEMBER:
2227             return "entity." ~ m ~ "().loaded";
2228         case PropertyMemberKind.PROPERTY_MEMBER:
2229             return "entity." ~ m ~ ".loaded";
2230         case PropertyMemberKind.LAZY_MEMBER:
2231             return "entity." ~ m ~ ".loaded";
2232         case PropertyMemberKind.UNSUPPORTED_MEMBER:
2233             assert(false, "Unsupported member kind " ~ T.stringof ~ "." ~ m);
2234     }
2235 }
2236 
2237 
2238 // TODO: minimize duplication of code in getXXXtoXXXPropertyDef
2239 
2240 /// generate source code for creation of OneToOne definition
2241 string getOneToOnePropertyDef(T, immutable string m)() {
2242     immutable string referencedEntityName = getPropertyReferencedEntityName!(T,m);
2243     immutable string referencedClassName = getPropertyReferencedClassName!(T,m);
2244     immutable string referencedPropertyName = getOneToOneReferencedPropertyName!(T,m);
2245     immutable string entityClassName = fullyQualifiedName!T;
2246     immutable string propertyName = getPropertyName!(T,m);
2247     static assert (propertyName != null, "Cannot determine property name for member " ~ m ~ " of type " ~ T.stringof);
2248     static assert (!hasOneOfMemberAnnotations!(T, m, Column, Id, Generated, Generator, ManyToOne, ManyToMany), entityClassName ~ "." ~ propertyName ~ ": OneToOne property cannot have Column, Id, Generated, Generator, ManyToOne, ManyToMany annotation");
2249     immutable bool isLazy = isLazyMember!(T,m);
2250     immutable string columnName = getJoinColumnName!(T, m);
2251     immutable length = getColumnLength!(T, m)();
2252     immutable bool hasNull = hasMemberAnnotation!(T,m, Null);
2253     immutable bool hasNotNull = hasMemberAnnotation!(T,m, NotNull);
2254     immutable bool nullable = hasNull ? true : (hasNotNull ? false : true); //canColumnTypeHoldNulls!(T.m)
2255     immutable string unique = quoteString(getUniqueIndexName!(T, m));
2256     immutable string typeName = "new EntityType(cast(immutable TypeInfo_Class)" ~ entityClassName ~ ".classinfo, \"" ~ entityClassName ~ "\")"; //getColumnTypeName!(T, m)();
2257     immutable string propertyReadCode = getPropertyReadCode!(T,m);
2258     immutable string datasetReadCode = null; //getColumnTypeDatasetReadCode!(T,m)();
2259     immutable string propertyWriteCode = null; //getPropertyWriteCode!(T,m)();
2260     immutable string datasetWriteCode = null; //getColumnTypeDatasetWriteCode!(T,m)();
2261     immutable string propertyVariantSetCode = getEmbeddedPropertyVariantWriteCode!(T, m, referencedClassName); // getPropertyVariantWriteCode!(T,m)();
2262     immutable string propertyVariantGetCode = "Variant(" ~ propertyReadCode ~ " is null ? null : " ~ propertyReadCode ~ ")"; //getPropertyVariantReadCode!(T,m)();
2263     immutable string propertyObjectSetCode = getPropertyObjectWriteCode!(T,m, referencedClassName); // getPropertyVariantWriteCode!(T,m)();
2264     immutable string propertyObjectGetCode = propertyReadCode; //getPropertyVariantReadCode!(T,m)();
2265     immutable string keyIsSetCode = null; //getColumnTypeKeyIsSetCode!(T,m)();
2266     immutable string isNullCode = propertyReadCode ~ " is null";
2267     immutable string copyFieldCode = getPropertyCopyCode!(T,m);
2268     //  pragma(msg, "property read: " ~ propertyReadCode);
2269     //  pragma(msg, "property write: " ~ propertyWriteCode);
2270     //  pragma(msg, "variant get: " ~ propertyVariantGetCode);
2271     immutable string readerFuncDef = "null";
2272     immutable string writerFuncDef = "null";
2273     immutable string getVariantFuncDef = 
2274         "\n" ~
2275             "function(Object obj) { \n" ~ 
2276             "    " ~ entityClassName ~ " entity = cast(" ~ entityClassName ~ ")obj; \n" ~
2277             "    return " ~ propertyVariantGetCode ~ "; \n" ~
2278             " }\n";
2279     immutable string setVariantFuncDef = 
2280         "\n" ~
2281             "function(Object obj, Variant value) { \n" ~ 
2282             "    " ~ entityClassName ~ " entity = cast(" ~ entityClassName ~ ")obj; \n" ~
2283             "    " ~ propertyVariantSetCode ~ "\n" ~
2284             " }\n";
2285     immutable string keyIsSetFuncDef = "\n" ~
2286         "function(Object obj) { \n" ~ 
2287             "    return false;\n" ~
2288             " }\n";
2289     immutable string isNullFuncDef = "\n" ~
2290         "function(Object obj) { \n" ~ 
2291             "    " ~ entityClassName ~ " entity = cast(" ~ entityClassName ~ ")obj; \n" ~
2292             "    return " ~ isNullCode ~ ";\n" ~
2293             " }\n";
2294     immutable string getObjectFuncDef = 
2295         "\n" ~
2296             "function(Object obj) { \n" ~ 
2297             "    " ~ entityClassName ~ " entity = cast(" ~ entityClassName ~ ")obj; \n" ~
2298             "    assert(entity !is null);\n" ~
2299             "    return " ~ propertyObjectGetCode ~ "; \n" ~
2300             " }\n";
2301     immutable string setObjectFuncDef = 
2302         "\n" ~
2303             "function(Object obj, Object value) { \n" ~ 
2304             "    " ~ entityClassName ~ " entity = cast(" ~ entityClassName ~ ")obj; \n" ~
2305             "    " ~ propertyObjectSetCode ~ "\n" ~
2306             " }\n";
2307     immutable string copyFuncDef = 
2308         "\n" ~
2309             "function(Object to, Object from) { \n" ~ 
2310             "    " ~ entityClassName ~ " toentity = cast(" ~ entityClassName ~ ")to; \n" ~
2311             "    " ~ entityClassName ~ " fromentity = cast(" ~ entityClassName ~ ")from; \n" ~
2312             "    " ~ copyFieldCode ~ "\n" ~
2313             " }\n";
2314     immutable string getCollectionFuncDef = "null";
2315     immutable string setCollectionFuncDef = "null";
2316     immutable string setObjectDelegateFuncDef = !isLazy ? "null" :
2317             "\n" ~
2318             "function(Object obj, Object delegate() loader) { \n" ~ 
2319             "    " ~ entityClassName ~ " entity = cast(" ~ entityClassName ~ ")obj; \n" ~
2320             "    " ~ getLazyPropertyObjectWriteCode!(T,m) ~ "\n" ~
2321             " }\n";
2322     immutable string setCollectionDelegateFuncDef = "null";
2323     immutable string isLoadedFuncDef = !isLazy ? "null" : 
2324             "\n" ~
2325             "function(Object obj) { \n" ~ 
2326             "    " ~ entityClassName ~ " entity = cast(" ~ entityClassName ~ ")obj; \n" ~
2327             "    return " ~ getLazyPropertyLoadedCode!(T,m) ~ ";\n" ~
2328             " }\n";
2329     
2330     return "    new PropertyInfo(" ~
2331             quoteString(propertyName) ~ ", " ~ 
2332             quoteString(columnName) ~ ", " ~ 
2333             typeName ~ ", " ~ 
2334             format("%s",length) ~ ", " ~ 
2335             "false, " ~ // id
2336             "false, " ~ // generated
2337             quoteBool(nullable) ~ ", " ~ 
2338             unique ~ ", " ~
2339             "RelationType.OneToOne, " ~
2340             quoteString(referencedEntityName)  ~ ", " ~ 
2341             quoteString(referencedPropertyName)  ~ ", " ~ 
2342             readerFuncDef ~ ", " ~
2343             writerFuncDef ~ ", " ~
2344             getVariantFuncDef ~ ", " ~
2345             setVariantFuncDef ~ ", " ~
2346             keyIsSetFuncDef ~ ", " ~
2347             isNullFuncDef ~ ", " ~
2348             copyFuncDef ~ ", " ~
2349             "null, " ~ // generatorFunc
2350             getObjectFuncDef ~ ", " ~
2351             setObjectFuncDef ~ ", " ~
2352             getCollectionFuncDef ~ ", " ~
2353             setCollectionFuncDef ~ ", " ~
2354             setObjectDelegateFuncDef ~ ", " ~
2355             setCollectionDelegateFuncDef ~ ", " ~
2356             isLoadedFuncDef ~ ", " ~
2357             quoteBool(isLazy) ~ ", " ~ // lazy
2358             "false" ~ // collection
2359             ")";
2360 }
2361 
2362 /// generate source code for creation of ManyToOne definition
2363 string getManyToOnePropertyDef(T, immutable string m)() {
2364     immutable string referencedEntityName = getPropertyReferencedEntityName!(T,m);
2365     immutable string referencedClassName = getPropertyReferencedClassName!(T,m);
2366     immutable string referencedPropertyName = getOneToOneReferencedPropertyName!(T,m);
2367     immutable string entityClassName = fullyQualifiedName!T;
2368     immutable string propertyName = getPropertyName!(T,m);
2369     static assert (propertyName != null, "Cannot determine property name for member " ~ m ~ " of type " ~ T.stringof);
2370     static assert (!hasOneOfMemberAnnotations!(T, m, Column, Id, Generated, Generator, OneToOne, ManyToMany), entityClassName ~ "." ~ propertyName ~ ": ManyToOne property cannot have Column, Id, Generated, Generator, OneToOne, ManyToMany annotation");
2371     immutable string columnName = applyDefault(getJoinColumnName!(T, m),camelCaseToUnderscoreDelimited(referencedEntityName) ~ "_fk");
2372     static assert (columnName != null, "ManyToOne property " ~ m ~ " has no JoinColumn name");
2373     immutable bool isLazy = isLazyMember!(T,m);
2374     immutable length = getColumnLength!(T, m);
2375     immutable bool hasNull = hasMemberAnnotation!(T,m, Null);
2376     immutable bool hasNotNull = hasMemberAnnotation!(T,m, NotNull);
2377     immutable bool nullable = hasNull ? true : (hasNotNull ? false : true); //canColumnTypeHoldNulls!(T.m)
2378     immutable string unique = quoteString(getUniqueIndexName!(T, m));
2379     immutable string typeName = "new EntityType(cast(immutable TypeInfo_Class)" ~ entityClassName ~ ".classinfo, \"" ~ entityClassName ~ "\")"; //getColumnTypeName!(T, m)();
2380     immutable string propertyReadCode = getPropertyReadCode!(T,m)();
2381     immutable string datasetReadCode = null; //getColumnTypeDatasetReadCode!(T,m)();
2382     immutable string propertyWriteCode = null; //getPropertyWriteCode!(T,m)();
2383     immutable string datasetWriteCode = null; //getColumnTypeDatasetWriteCode!(T,m)();
2384     immutable string propertyVariantSetCode = getEmbeddedPropertyVariantWriteCode!(T, m, referencedClassName); // getPropertyVariantWriteCode!(T,m)();
2385     immutable string propertyVariantGetCode = "Variant(" ~ propertyReadCode ~ " is null ? null : " ~ propertyReadCode ~ ")"; //getPropertyVariantReadCode!(T,m)();
2386     immutable string propertyObjectSetCode = getPropertyObjectWriteCode!(T,m, referencedClassName); // getPropertyVariantWriteCode!(T,m)();
2387     immutable string propertyObjectGetCode = propertyReadCode; //getPropertyVariantReadCode!(T,m)();
2388     immutable string keyIsSetCode = null; //getColumnTypeKeyIsSetCode!(T,m)();
2389     immutable string isNullCode = propertyReadCode ~ " is null";
2390     immutable string copyFieldCode = getPropertyCopyCode!(T,m);
2391     //  pragma(msg, "property read: " ~ propertyReadCode);
2392     //  pragma(msg, "property write: " ~ propertyWriteCode);
2393     //  pragma(msg, "variant get: " ~ propertyVariantGetCode);
2394     immutable string readerFuncDef = "null";
2395     immutable string writerFuncDef = "null";
2396     immutable string getVariantFuncDef = 
2397         "\n" ~
2398             "function(Object obj) { \n" ~ 
2399             "    " ~ entityClassName ~ " entity = cast(" ~ entityClassName ~ ")obj; \n" ~
2400             "    return " ~ propertyVariantGetCode ~ "; \n" ~
2401             " }\n";
2402     immutable string setVariantFuncDef = 
2403         "\n" ~
2404             "function(Object obj, Variant value) { \n" ~ 
2405             "    " ~ entityClassName ~ " entity = cast(" ~ entityClassName ~ ")obj; \n" ~
2406             "    " ~ propertyVariantSetCode ~ "\n" ~
2407             " }\n";
2408     immutable string keyIsSetFuncDef = "\n" ~
2409         "function(Object obj) { \n" ~ 
2410             "    return false;\n" ~
2411             " }\n";
2412     immutable string isNullFuncDef = "\n" ~
2413         "function(Object obj) { \n" ~ 
2414             "    " ~ entityClassName ~ " entity = cast(" ~ entityClassName ~ ")obj; \n" ~
2415             "    return " ~ isNullCode ~ ";\n" ~
2416             " }\n";
2417     immutable string getObjectFuncDef = 
2418         "\n" ~
2419             "function(Object obj) { \n" ~ 
2420             "    //writeln(\"Inside getObjectFunc\"); \n" ~
2421             "    " ~ entityClassName ~ " entity = cast(" ~ entityClassName ~ ")obj; \n" ~
2422             "    assert(entity !is null);\n" ~
2423             "    Object res = " ~ propertyObjectGetCode ~ "; \n" ~
2424             "    //writeln(res is null ? \"obj is null\" : \"obj is not null\"); \n" ~
2425             "    return res; \n" ~
2426             " }\n";
2427     immutable string setObjectFuncDef = 
2428         "\n" ~
2429             "function(Object obj, Object value) { \n" ~ 
2430             "    " ~ entityClassName ~ " entity = cast(" ~ entityClassName ~ ")obj; \n" ~
2431             "    " ~ propertyObjectSetCode ~ "\n" ~
2432             " }\n";
2433     immutable string copyFuncDef = 
2434         "\n" ~
2435             "function(Object to, Object from) { \n" ~ 
2436             "    " ~ entityClassName ~ " toentity = cast(" ~ entityClassName ~ ")to; \n" ~
2437             "    " ~ entityClassName ~ " fromentity = cast(" ~ entityClassName ~ ")from; \n" ~
2438             "    " ~ copyFieldCode ~ "\n" ~
2439             " }\n";
2440     immutable string getCollectionFuncDef = "null";
2441     immutable string setCollectionFuncDef = "null";
2442     immutable string setObjectDelegateFuncDef = !isLazy ? "null" :
2443         "\n" ~
2444         "function(Object obj, Object delegate() loader) { \n" ~ 
2445             "    " ~ entityClassName ~ " entity = cast(" ~ entityClassName ~ ")obj; \n" ~
2446             "    " ~ getLazyPropertyObjectWriteCode!(T,m) ~ "\n" ~
2447             " }\n";
2448     immutable string setCollectionDelegateFuncDef = "null";
2449     immutable string isLoadedFuncDef = !isLazy ? "null" : 
2450     "\n" ~
2451         "function(Object obj) { \n" ~ 
2452             "    " ~ entityClassName ~ " entity = cast(" ~ entityClassName ~ ")obj; \n" ~
2453             "    return " ~ getLazyPropertyLoadedCode!(T,m) ~ ";\n" ~
2454             " }\n";
2455 
2456     return "    new PropertyInfo(" ~
2457             quoteString(propertyName) ~ ", " ~ 
2458             quoteString(columnName) ~ ", " ~ 
2459             typeName ~ ", " ~ 
2460             format("%s",length) ~ ", " ~ 
2461             "false, " ~ // id
2462             "false, " ~ // generated
2463             quoteBool(nullable) ~ ", " ~ 
2464             unique ~ ", " ~
2465             "RelationType.ManyToOne, " ~
2466             quoteString(referencedEntityName)  ~ ", " ~ 
2467             quoteString(referencedPropertyName)  ~ ", " ~ 
2468             readerFuncDef ~ ", " ~
2469             writerFuncDef ~ ", " ~
2470             getVariantFuncDef ~ ", " ~
2471             setVariantFuncDef ~ ", " ~
2472             keyIsSetFuncDef ~ ", " ~
2473             isNullFuncDef ~ ", " ~
2474             copyFuncDef ~ ", " ~
2475             "null, " ~ // generatorFunc
2476             getObjectFuncDef ~ ", " ~
2477             setObjectFuncDef ~ ", " ~
2478             getCollectionFuncDef ~ ", " ~
2479             setCollectionFuncDef ~ ", " ~
2480             setObjectDelegateFuncDef ~ ", " ~
2481             setCollectionDelegateFuncDef ~ ", " ~
2482             isLoadedFuncDef ~ ", " ~
2483             quoteBool(isLazy) ~ ", " ~ // lazy
2484             "false" ~ // collection
2485             ")";
2486 }
2487 
2488 /// generate source code for creation of OneToMany definition
2489 string getOneToManyPropertyDef(T, immutable string m)() {
2490     immutable string referencedEntityName = getPropertyReferencedEntityName!(T,m);
2491     immutable string referencedClassName = getPropertyReferencedClassName!(T,m);
2492     immutable string referencedPropertyName = getOneToManyReferencedPropertyName!(T,m);
2493     static assert (referencedPropertyName != null, "OneToMany should have referenced property name parameter");
2494     immutable string entityClassName = fullyQualifiedName!T;
2495     immutable string propertyName = getPropertyName!(T,m)();
2496     static assert (propertyName != null, "Cannot determine property name for member " ~ m ~ " of type " ~ T.stringof);
2497     static assert (!hasOneOfMemberAnnotations!(T, m, Column, Id, Generated, Generator, OneToOne, ManyToMany), entityClassName ~ "." ~ propertyName ~ ": OneToMany property cannot have Column, Id, Generated, Generator, OneToOne, ManyToMany or Embedded annotation");
2498     immutable string columnName = getJoinColumnName!(T, m)();
2499     immutable bool isCollection = isCollectionMember!(T,m);
2500     static assert (isCollection, "OneToMany property " ~ m ~ " should be array of objects or LazyCollection");
2501     static assert (columnName == null, "OneToMany property " ~ m ~ " should not have JoinColumn name");
2502     immutable bool isLazy = isLazyMember!(T,m) || isLazyCollectionMember!(T,m);
2503     immutable length = getColumnLength!(T, m);
2504     immutable bool hasNull = hasMemberAnnotation!(T,m, Null);
2505     immutable bool hasNotNull = hasMemberAnnotation!(T,m, NotNull);
2506     immutable bool nullable = hasNull ? true : (hasNotNull ? false : true); //canColumnTypeHoldNulls!(T.m)
2507     immutable string unique = quoteString(getUniqueIndexName!(T, m));
2508     immutable string typeName = "new EntityType(cast(immutable TypeInfo_Class)" ~ entityClassName ~ ".classinfo, \"" ~ entityClassName ~ "\")"; //getColumnTypeName!(T, m)();
2509     immutable string propertyReadCode = getPropertyReadCode!(T,m)();
2510     immutable string datasetReadCode = null; //getColumnTypeDatasetReadCode!(T,m)();
2511     immutable string propertyWriteCode = null; //getPropertyWriteCode!(T,m)();
2512     immutable string datasetWriteCode = null; //getColumnTypeDatasetWriteCode!(T,m)();
2513     immutable string propertyVariantSetCode = getCollectionPropertyVariantWriteCode!(T, m, referencedClassName); // getPropertyVariantWriteCode!(T,m)();
2514     immutable string propertyVariantGetCode = "Variant(" ~ propertyReadCode ~ " is null ? null : " ~ propertyReadCode ~ ")"; //getPropertyVariantReadCode!(T,m)();
2515     //pragma(msg, "propertyVariantGetCode: " ~ propertyVariantGetCode);
2516     //pragma(msg, "propertyVariantSetCode: " ~ propertyVariantSetCode);
2517     immutable string propertyObjectSetCode = getPropertyCollectionWriteCode!(T,m, referencedClassName); // getPropertyVariantWriteCode!(T,m)();
2518     immutable string propertyObjectGetCode = propertyReadCode; //getPropertyVariantReadCode!(T,m)();
2519     immutable string keyIsSetCode = null; //getColumnTypeKeyIsSetCode!(T,m)();
2520     immutable string isNullCode = propertyReadCode ~ " is null";
2521     immutable string copyFieldCode = getPropertyCopyCode!(T,m);
2522     //  pragma(msg, "property read: " ~ propertyReadCode);
2523     //  pragma(msg, "property write: " ~ propertyWriteCode);
2524     //  pragma(msg, "variant get: " ~ propertyVariantGetCode);
2525     immutable string readerFuncDef = "null";
2526     immutable string writerFuncDef = "null";
2527     immutable string getVariantFuncDef = 
2528         "\n" ~
2529             "function(Object obj) { \n" ~ 
2530             "    " ~ entityClassName ~ " entity = cast(" ~ entityClassName ~ ")obj; \n" ~
2531             "    return " ~ propertyVariantGetCode ~ "; \n" ~
2532             " }\n";
2533     immutable string setVariantFuncDef = 
2534         "\n" ~
2535             "function(Object obj, Variant value) { \n" ~ 
2536             "    " ~ entityClassName ~ " entity = cast(" ~ entityClassName ~ ")obj; \n" ~
2537             "    " ~ propertyVariantSetCode ~ "\n" ~
2538             " }\n";
2539     immutable string keyIsSetFuncDef = "\n" ~
2540         "function(Object obj) { \n" ~ 
2541             "    return false;\n" ~
2542             " }\n";
2543     immutable string isNullFuncDef = "\n" ~
2544         "function(Object obj) { \n" ~ 
2545             "    " ~ entityClassName ~ " entity = cast(" ~ entityClassName ~ ")obj; \n" ~
2546             "    return " ~ isNullCode ~ ";\n" ~
2547             " }\n";
2548     immutable string getObjectFuncDef = "null";
2549     immutable string setObjectFuncDef = "null";
2550     immutable string copyFuncDef = 
2551         "\n" ~
2552             "function(Object to, Object from) { \n" ~ 
2553             "    " ~ entityClassName ~ " toentity = cast(" ~ entityClassName ~ ")to; \n" ~
2554             "    " ~ entityClassName ~ " fromentity = cast(" ~ entityClassName ~ ")from; \n" ~
2555             "    " ~ copyFieldCode ~ "\n" ~
2556             " }\n";
2557     immutable string getCollectionFuncDef = 
2558         "\n" ~
2559         "function(Object obj) { \n" ~ 
2560             "    " ~ entityClassName ~ " entity = cast(" ~ entityClassName ~ ")obj; \n" ~
2561             "    assert(entity !is null);\n" ~
2562             "    return cast(Object[])" ~ propertyObjectGetCode ~ "; \n" ~
2563             " }\n";
2564     immutable string setCollectionFuncDef = 
2565         "\n" ~
2566         "function(Object obj, Object[] value) { \n" ~ 
2567             "    " ~ entityClassName ~ " entity = cast(" ~ entityClassName ~ ")obj; \n" ~
2568             "    " ~ propertyObjectSetCode ~ "\n" ~
2569             " }\n";
2570     immutable string setObjectDelegateFuncDef = "null";
2571     immutable string setCollectionDelegateFuncDef = !isLazy ? "null" :
2572     "\n" ~
2573         "function(Object obj, Object[] delegate() loader) { \n" ~ 
2574             "    " ~ entityClassName ~ " entity = cast(" ~ entityClassName ~ ")obj; \n" ~
2575             "    " ~ getLazyPropertyObjectWriteCode!(T,m) ~ "\n" ~
2576             " }\n";
2577     immutable string isLoadedFuncDef = !isLazy ? "null" : 
2578     "\n" ~
2579         "function(Object obj) { \n" ~ 
2580             "    " ~ entityClassName ~ " entity = cast(" ~ entityClassName ~ ")obj; \n" ~
2581             "    return " ~ getLazyPropertyLoadedCode!(T,m) ~ ";\n" ~
2582             " }\n";
2583 
2584     return "    new PropertyInfo(" ~
2585             quoteString(propertyName) ~ ", " ~ 
2586             quoteString(columnName) ~ ", " ~ 
2587             typeName ~ ", " ~ 
2588             format("%s",length) ~ ", " ~ 
2589             "false"  ~ ", " ~ // id
2590             "false"  ~ ", " ~ // generated
2591             quoteBool(nullable) ~ ", " ~ 
2592             unique ~ ", " ~
2593             "RelationType.OneToMany, " ~
2594             quoteString(referencedEntityName)  ~ ", " ~ 
2595             quoteString(referencedPropertyName)  ~ ", " ~ 
2596             readerFuncDef ~ ", " ~
2597             writerFuncDef ~ ", " ~
2598             getVariantFuncDef ~ ", " ~
2599             setVariantFuncDef ~ ", " ~
2600             keyIsSetFuncDef ~ ", " ~
2601             isNullFuncDef ~ ", " ~
2602             copyFuncDef ~ ", " ~
2603             "null, " ~ // generatorFunc
2604             getObjectFuncDef ~ ", " ~
2605             setObjectFuncDef ~ ", " ~
2606             getCollectionFuncDef ~ ", " ~
2607             setCollectionFuncDef ~ ", " ~
2608             setObjectDelegateFuncDef ~ ", " ~
2609             setCollectionDelegateFuncDef ~ ", " ~
2610             isLoadedFuncDef ~ ", " ~
2611             quoteBool(isLazy) ~ ", " ~ // lazy
2612             "true" ~ // is collection
2613             ")";
2614 }
2615 
2616 /// generate source code for creation of ManyToMany definition
2617 string getManyToManyPropertyDef(T, immutable string m)() {
2618     immutable string referencedEntityName = getPropertyReferencedEntityName!(T,m);
2619     immutable string referencedClassName = getPropertyReferencedClassName!(T,m);
2620     immutable string entityClassName = fullyQualifiedName!T;
2621     immutable string propertyName = getPropertyName!(T,m);
2622     static assert (propertyName != null, "Cannot determine property name for member " ~ m ~ " of type " ~ T.stringof);
2623     static assert (!hasOneOfMemberAnnotations!(T, m, Column, Id, Generated, Generator, OneToOne, OneToMany), entityClassName ~ "." ~ propertyName ~ ": ManyToMany property cannot have Column, Id, Generated, Generator, OneToOne, OneToMany annotation");
2624     immutable string columnName = getJoinColumnName!(T, m);
2625     immutable string joinTableName = getJoinTableName!(T, m);
2626     immutable string joinColumn1 = getJoinTableColumn1!(T, m);
2627     immutable string joinColumn2 = getJoinTableColumn2!(T, m);
2628     immutable string joinTableCode = JoinTableInfo.generateJoinTableCode(joinTableName, joinColumn1, joinColumn2);
2629     immutable bool isCollection = isCollectionMember!(T,m);
2630     static assert (isCollection, "ManyToMany property " ~ m ~ " should be array of objects or LazyCollection");
2631     static assert (columnName == null, "ManyToMany property " ~ m ~ " should not have JoinColumn name");
2632     immutable bool isLazy = isLazyMember!(T,m) || isLazyCollectionMember!(T,m);
2633     immutable length = getColumnLength!(T, m);
2634     immutable bool hasNull = hasMemberAnnotation!(T,m, Null);
2635     immutable bool hasNotNull = hasMemberAnnotation!(T,m, NotNull);
2636     immutable bool nullable = hasNull ? true : (hasNotNull ? false : true); //canColumnTypeHoldNulls!(T.m)
2637     immutable string unique = quoteString(getUniqueIndexName!(T, m));
2638     immutable string typeName = "new EntityType(cast(immutable TypeInfo_Class)" ~ entityClassName ~ ".classinfo, \"" ~ entityClassName ~ "\")"; //getColumnTypeName!(T, m)();
2639     immutable string propertyReadCode = getPropertyReadCode!(T,m);
2640     immutable string datasetReadCode = null; //getColumnTypeDatasetReadCode!(T,m)();
2641     immutable string propertyWriteCode = null; //getPropertyWriteCode!(T,m)();
2642     immutable string datasetWriteCode = null; //getColumnTypeDatasetWriteCode!(T,m)();
2643     immutable string propertyVariantSetCode = getCollectionPropertyVariantWriteCode!(T, m, referencedClassName); // getPropertyVariantWriteCode!(T,m)();
2644     immutable string propertyVariantGetCode = "Variant(" ~ propertyReadCode ~ " is null ? null : " ~ propertyReadCode ~ ")"; //getPropertyVariantReadCode!(T,m)();
2645     //pragma(msg, "propertyVariantGetCode: " ~ propertyVariantGetCode);
2646     //pragma(msg, "propertyVariantSetCode: " ~ propertyVariantSetCode);
2647     immutable string propertyObjectSetCode = getPropertyCollectionWriteCode!(T,m, referencedClassName); // getPropertyVariantWriteCode!(T,m)();
2648     immutable string propertyObjectGetCode = propertyReadCode; //getPropertyVariantReadCode!(T,m)();
2649     immutable string keyIsSetCode = null; //getColumnTypeKeyIsSetCode!(T,m)();
2650     immutable string isNullCode = propertyReadCode ~ " is null";
2651     immutable string copyFieldCode = getPropertyCopyCode!(T,m);
2652     immutable string readerFuncDef = "null";
2653     immutable string writerFuncDef = "null";
2654     immutable string getVariantFuncDef = 
2655         "\n" ~
2656             "function(Object obj) { \n" ~ 
2657             "    " ~ entityClassName ~ " entity = cast(" ~ entityClassName ~ ")obj; \n" ~
2658             "    return " ~ propertyVariantGetCode ~ "; \n" ~
2659             " }\n";
2660     immutable string setVariantFuncDef = 
2661         "\n" ~
2662             "function(Object obj, Variant value) { \n" ~ 
2663             "    " ~ entityClassName ~ " entity = cast(" ~ entityClassName ~ ")obj; \n" ~
2664             "    " ~ propertyVariantSetCode ~ "\n" ~
2665             " }\n";
2666     immutable string keyIsSetFuncDef = "\n" ~
2667         "function(Object obj) { \n" ~ 
2668             "    return false;\n" ~
2669             " }\n";
2670     immutable string isNullFuncDef = "\n" ~
2671         "function(Object obj) { \n" ~ 
2672             "    " ~ entityClassName ~ " entity = cast(" ~ entityClassName ~ ")obj; \n" ~
2673             "    return " ~ isNullCode ~ ";\n" ~
2674             " }\n";
2675     immutable string getObjectFuncDef = "null";
2676     immutable string setObjectFuncDef = "null";
2677     immutable string copyFuncDef = 
2678         "\n" ~
2679             "function(Object to, Object from) { \n" ~ 
2680             "    " ~ entityClassName ~ " toentity = cast(" ~ entityClassName ~ ")to; \n" ~
2681             "    " ~ entityClassName ~ " fromentity = cast(" ~ entityClassName ~ ")from; \n" ~
2682             "    " ~ copyFieldCode ~ "\n" ~
2683             " }\n";
2684     immutable string getCollectionFuncDef = 
2685         "\n" ~
2686             "function(Object obj) { \n" ~ 
2687             "    " ~ entityClassName ~ " entity = cast(" ~ entityClassName ~ ")obj; \n" ~
2688             "    assert(entity !is null);\n" ~
2689             "    return cast(Object[])" ~ propertyObjectGetCode ~ "; \n" ~
2690             " }\n";
2691     immutable string setCollectionFuncDef = 
2692         "\n" ~
2693             "function(Object obj, Object[] value) { \n" ~ 
2694             "    " ~ entityClassName ~ " entity = cast(" ~ entityClassName ~ ")obj; \n" ~
2695             "    " ~ propertyObjectSetCode ~ "\n" ~
2696             " }\n";
2697     immutable string setObjectDelegateFuncDef = "null";
2698     immutable string setCollectionDelegateFuncDef = !isLazy ? "null" :
2699     "\n" ~
2700         "function(Object obj, Object[] delegate() loader) { \n" ~ 
2701             "    " ~ entityClassName ~ " entity = cast(" ~ entityClassName ~ ")obj; \n" ~
2702             "    " ~ getLazyPropertyObjectWriteCode!(T,m) ~ "\n" ~
2703             " }\n";
2704     immutable string isLoadedFuncDef = !isLazy ? "null" : 
2705     "\n" ~
2706         "function(Object obj) { \n" ~ 
2707             "    " ~ entityClassName ~ " entity = cast(" ~ entityClassName ~ ")obj; \n" ~
2708             "    return " ~ getLazyPropertyLoadedCode!(T,m) ~ ";\n" ~
2709             " }\n";
2710 
2711     return "    new PropertyInfo(" ~
2712             quoteString(propertyName) ~ ", " ~ 
2713             quoteString(columnName) ~ ", " ~ 
2714             typeName ~ ", " ~ 
2715             format("%s",length) ~ ", " ~ 
2716             "false"  ~ ", " ~ // id
2717             "false"  ~ ", " ~ // generated
2718             quoteBool(nullable) ~ ", " ~ 
2719             unique ~ ", " ~
2720             "RelationType.ManyToMany, " ~
2721             quoteString(referencedEntityName)  ~ ", " ~ 
2722             "null, " ~ //referencedPropertyName
2723             readerFuncDef ~ ", " ~
2724             writerFuncDef ~ ", " ~
2725             getVariantFuncDef ~ ", " ~
2726             setVariantFuncDef ~ ", " ~
2727             keyIsSetFuncDef ~ ", " ~
2728             isNullFuncDef ~ ", " ~
2729             copyFuncDef ~ ", " ~
2730             "null, " ~ // generatorFunc
2731             getObjectFuncDef ~ ", " ~
2732             setObjectFuncDef ~ ", " ~
2733             getCollectionFuncDef ~ ", " ~
2734             setCollectionFuncDef ~ ", " ~
2735             setObjectDelegateFuncDef ~ ", " ~
2736             setCollectionDelegateFuncDef ~ ", " ~
2737             isLoadedFuncDef ~ ", " ~
2738             quoteBool(isLazy) ~ ", " ~ // lazy
2739             "true" ~ ", " ~ // is collection
2740             joinTableCode ~
2741             ")";
2742 }
2743 
2744 /// generate source code for creation of Embedded definition
2745 string getEmbeddedPropertyDef(T, immutable string m)() {
2746     immutable string referencedEntityName = getPropertyEmbeddedEntityName!(T,m);
2747     immutable string referencedClassName = getPropertyEmbeddedClassName!(T,m);
2748     immutable string entityClassName = fullyQualifiedName!T;
2749     immutable string propertyName = getPropertyName!(T,m);
2750     static assert (propertyName != null, "Cannot determine property name for member " ~ m ~ " of type " ~ T.stringof);
2751     static assert (!hasOneOfMemberAnnotations!(T, m, Column, Id, Generated, Generator, ManyToOne, ManyToMany, OneToOne), entityClassName ~ "." ~ propertyName ~ ": Embedded property cannot have Column, Id, Generated, OneToOne, ManyToOne, ManyToMany annotation");
2752     immutable string columnName = getColumnName!(T, m);
2753     immutable length = getColumnLength!(T, m);
2754     immutable bool hasNull = hasMemberAnnotation!(T, m, Null);
2755     immutable bool hasNotNull = hasMemberAnnotation!(T, m, NotNull);
2756     immutable bool nullable = hasNull ? true : (hasNotNull ? false : true); //canColumnTypeHoldNulls!(T.m)
2757     immutable string unique = quoteString(getUniqueIndexName!(T, m));
2758     immutable string typeName = "new EntityType(cast(immutable TypeInfo_Class)" ~ entityClassName ~ ".classinfo, \"" ~ entityClassName ~ "\")"; //getColumnTypeName!(T, m)();
2759     immutable string propertyReadCode = getPropertyReadCode!(T,m);
2760     immutable string datasetReadCode = null; //getColumnTypeDatasetReadCode!(T,m)();
2761     immutable string propertyWriteCode = null; //getPropertyWriteCode!(T,m)();
2762     immutable string datasetWriteCode = null; //getColumnTypeDatasetWriteCode!(T,m)();
2763     immutable string propertyVariantSetCode = getEmbeddedPropertyVariantWriteCode!(T, m, referencedClassName); // getPropertyVariantWriteCode!(T,m)();
2764     immutable string propertyVariantGetCode = "Variant(" ~ propertyReadCode ~ " is null ? null : " ~ propertyReadCode ~ ")"; //getPropertyVariantReadCode!(T,m)();
2765     immutable string propertyObjectSetCode = getPropertyObjectWriteCode!(T,m, referencedClassName); // getPropertyVariantWriteCode!(T,m)();
2766     immutable string propertyObjectGetCode = propertyReadCode; //getPropertyVariantReadCode!(T,m)();
2767     immutable string keyIsSetCode = null; //getColumnTypeKeyIsSetCode!(T,m)();
2768     immutable string isNullCode = propertyReadCode ~ " is null";
2769     immutable string copyFieldCode = getPropertyCopyCode!(T,m);
2770     //  pragma(msg, "property read: " ~ propertyReadCode);
2771     //  pragma(msg, "property write: " ~ propertyWriteCode);
2772     //  pragma(msg, "variant get: " ~ propertyVariantGetCode);
2773     immutable string readerFuncDef = "null";
2774     immutable string writerFuncDef = "null";
2775     immutable string getVariantFuncDef = 
2776         "\n" ~
2777         "function(Object obj) { \n" ~ 
2778             "    " ~ entityClassName ~ " entity = cast(" ~ entityClassName ~ ")obj; \n" ~
2779             "    return " ~ propertyVariantGetCode ~ "; \n" ~
2780             " }\n";
2781     immutable string setVariantFuncDef = 
2782         "\n" ~
2783         "function(Object obj, Variant value) { \n" ~ 
2784             "    " ~ entityClassName ~ " entity = cast(" ~ entityClassName ~ ")obj; \n" ~
2785             "    " ~ propertyVariantSetCode ~ "\n" ~
2786             " }\n";
2787     immutable string keyIsSetFuncDef = "\n" ~
2788         "function(Object obj) { \n" ~ 
2789             "    return false;\n" ~
2790             " }\n";
2791     immutable string isNullFuncDef = "\n" ~
2792         "function(Object obj) { \n" ~ 
2793             "    " ~ entityClassName ~ " entity = cast(" ~ entityClassName ~ ")obj; \n" ~
2794             "    return " ~ isNullCode ~ ";\n" ~
2795             " }\n";
2796     immutable string getObjectFuncDef = 
2797         "\n" ~
2798             "function(Object obj) { \n" ~ 
2799             "    " ~ entityClassName ~ " entity = cast(" ~ entityClassName ~ ")obj; \n" ~
2800             "    assert(entity !is null);\n" ~
2801             "    return " ~ propertyObjectGetCode ~ "; \n" ~
2802             " }\n";
2803     immutable string setObjectFuncDef = 
2804         "\n" ~
2805             "function(Object obj, Object value) { \n" ~ 
2806             "    " ~ entityClassName ~ " entity = cast(" ~ entityClassName ~ ")obj; \n" ~
2807             "    " ~ propertyObjectSetCode ~ "\n" ~
2808             " }\n";
2809     immutable string copyFuncDef = 
2810         "\n" ~
2811             "function(Object to, Object from) { \n" ~ 
2812             "    " ~ entityClassName ~ " toentity = cast(" ~ entityClassName ~ ")to; \n" ~
2813             "    " ~ entityClassName ~ " fromentity = cast(" ~ entityClassName ~ ")from; \n" ~
2814             "    " ~ copyFieldCode ~ "\n" ~
2815             " }\n";
2816     
2817     return "    new PropertyInfo(" ~ 
2818             quoteString(propertyName) ~ ", " ~ 
2819             quoteString(columnName) ~ ", " ~ 
2820             typeName ~ ", " ~ 
2821             format("%s",length) ~ ", " ~ 
2822             "false, " ~ // id
2823             "false, " ~ // generated
2824             quoteBool(nullable) ~ ", " ~ 
2825             unique ~ ", " ~
2826             "RelationType.Embedded, " ~
2827             quoteString(referencedEntityName)  ~ ", " ~ 
2828             "null, \n" ~
2829             readerFuncDef ~ ", " ~
2830             writerFuncDef ~ ", " ~
2831             getVariantFuncDef ~ ", " ~
2832             setVariantFuncDef ~ ", " ~
2833             keyIsSetFuncDef ~ ", " ~
2834             isNullFuncDef ~ ", " ~
2835             copyFuncDef ~ ", " ~
2836             "null, " ~ // generatorFunc
2837             getObjectFuncDef ~ ", " ~
2838             setObjectFuncDef ~ 
2839             ")";
2840 }
2841 
2842 /// generate source code for creation of simple property definition
2843 string getSimplePropertyDef(T, immutable string m)() {
2844     //getPropertyReferencedEntityName(
2845     immutable string entityClassName = fullyQualifiedName!T;
2846     immutable string propertyName = getPropertyName!(T,m);
2847     static assert (propertyName != null, "Cannot determine property name for member " ~ m ~ " of type " ~ T.stringof);
2848     static assert (!hasOneOfMemberAnnotations!(T, m, ManyToOne, OneToOne, ManyToMany), entityClassName ~ "." ~ propertyName ~ ": simple property cannot have OneToOne, ManyToOne, or ManyToMany annotation");
2849     immutable bool isIdPropertyName = propertyName == "id";
2850     immutable bool isEmbeddableClass = hasAnnotation!(T, Embeddable);
2851     immutable bool classHasKeyField = hasAnyKeyPropertyAnnotation!T;
2852     immutable string generatorCode = getGeneratorCode!(T, m);
2853     immutable bool hasKeyAnnotation = hasMemberAnnotation!(T, m, Id) || hasMemberAnnotation!(T, m, Generated) || generatorCode != null;
2854     immutable bool isId = hasKeyAnnotation || (isIdPropertyName && !classHasKeyField && !isEmbeddableClass);
2855     immutable bool isGenerated = hasMemberAnnotation!(T, m, Generated) || (!hasKeyAnnotation && isId);
2856     immutable string columnName = getColumnName!(T, m);
2857     static assert(!isGenerated || generatorCode == null, T.stringof ~ "." ~ m ~ ": You cannot mix @Generated and @Generator for the same property");
2858     immutable length = getColumnLength!(T, m)();
2859     immutable bool hasNull = hasMemberAnnotation!(T,m,Null);
2860     immutable bool hasNotNull = hasMemberAnnotation!(T,m,NotNull);
2861     immutable bool nullable = hasNull ? true : (hasNotNull ? false : isColumnTypeNullableByDefault!(T, m)); //canColumnTypeHoldNulls!(T.m)
2862     immutable string unique = quoteString(getUniqueIndexName!(T, m));
2863     immutable string typeName = getColumnTypeName!(T, m, length);
2864     immutable string propertyReadCode = getPropertyReadCode!(T,m);
2865     immutable string datasetReadCode = getColumnTypeDatasetReadCode!(T,m);
2866     immutable string propertyWriteCode = getPropertyWriteCode!(T,m);
2867     immutable string datasetWriteCode = getColumnTypeDatasetWriteCode!(T,m);
2868     immutable string propertyVariantSetCode = getPropertyVariantWriteCode!(T,m);
2869     immutable string propertyVariantGetCode = getPropertyVariantReadCode!(T,m);
2870     immutable string keyIsSetCode = getColumnTypeKeyIsSetCode!(T,m);
2871     immutable string isNullCode = getColumnTypeIsNullCode!(T,m);
2872     immutable string copyFieldCode = getPropertyCopyCode!(T,m);
2873     immutable string readerFuncDef = "\n" ~
2874         "function(Object obj, DataSetReader r, int index) { \n" ~ 
2875             "    " ~ entityClassName ~ " entity = cast(" ~ entityClassName ~ ")obj; \n" ~
2876             "    " ~ propertyWriteCode ~ " \n" ~
2877             " }\n";
2878     immutable string writerFuncDef = "\n" ~
2879         "function(Object obj, DataSetWriter r, int index) { \n" ~ 
2880             "    " ~ entityClassName ~ " entity = cast(" ~ entityClassName ~ ")obj; \n" ~
2881             "    " ~ datasetWriteCode ~ " \n" ~
2882             " }\n";
2883     immutable string getVariantFuncDef = "\n" ~
2884         "function(Object obj) { \n" ~ 
2885             "    " ~ entityClassName ~ " entity = cast(" ~ entityClassName ~ ")obj; \n" ~
2886             "    return " ~ propertyVariantGetCode ~ "; \n" ~
2887             " }\n";
2888     immutable string setVariantFuncDef = "\n" ~
2889         "function(Object obj, Variant value) { \n" ~ 
2890             "    " ~ entityClassName ~ " entity = cast(" ~ entityClassName ~ ")obj; \n" ~
2891             "    " ~ propertyVariantSetCode ~ "\n" ~
2892             " }\n";
2893     immutable string keyIsSetFuncDef = "\n" ~
2894             "function(Object obj) { \n" ~ 
2895             "    " ~ entityClassName ~ " entity = cast(" ~ entityClassName ~ ")obj; \n" ~
2896             "    return " ~ keyIsSetCode ~ ";\n" ~
2897             " }\n";
2898     immutable string isNullFuncDef = "\n" ~
2899             "function(Object obj) { \n" ~ 
2900             "    " ~ entityClassName ~ " entity = cast(" ~ entityClassName ~ ")obj; \n" ~
2901             "    return " ~ isNullCode ~ ";\n" ~
2902             " }\n";
2903     immutable string copyFuncDef = 
2904             "\n" ~
2905             "function(Object to, Object from) { \n" ~ 
2906             "    " ~ entityClassName ~ " toentity = cast(" ~ entityClassName ~ ")to; \n" ~
2907             "    " ~ entityClassName ~ " fromentity = cast(" ~ entityClassName ~ ")from; \n" ~
2908             "    " ~ copyFieldCode ~ "\n" ~
2909             " }\n";
2910     immutable string generatorFuncDef = generatorCode is null ? "null" :
2911             "\n" ~
2912             "function(Connection conn, const PropertyInfo property) { \n" ~ 
2913             "    return Variant(" ~ generatorCode ~ ");\n" ~
2914             "}\n";
2915 
2916     static assert (typeName != null, "Cannot determine column type for member " ~ m ~ " of type " ~ T.stringof);
2917     return "    new PropertyInfo(" ~ 
2918             quoteString(propertyName) ~ ", " ~
2919             quoteString(columnName) ~ ", " ~ 
2920             typeName ~ ", " ~ 
2921             format("%s",length) ~ ", " ~ 
2922             quoteBool(isId)  ~ ", " ~ 
2923             quoteBool(isGenerated)  ~ ", " ~ 
2924             quoteBool(nullable) ~ ", " ~ 
2925             unique ~ ", " ~
2926             "RelationType.None, " ~ 
2927             "null, " ~ 
2928             "null, \n" ~
2929             readerFuncDef ~ ", " ~
2930             writerFuncDef ~ ", " ~
2931             getVariantFuncDef ~ ", " ~
2932             setVariantFuncDef ~ ", " ~
2933             keyIsSetFuncDef ~ ", " ~
2934             isNullFuncDef ~ ", " ~
2935             copyFuncDef ~ ", " ~
2936             generatorFuncDef ~
2937             ")";
2938 }
2939 
2940 /// creates "new PropertyInfo(...)" code to create property metadata for member m of class T
2941 string getPropertyDef(T, string m)() {
2942     immutable bool isObject = isObjectMember!(T, m);
2943     immutable bool isCollection = isCollectionMember!(T, m);
2944     immutable bool isEmbedded = isEmbeddedObjectMember!(T, m);
2945     immutable bool isOneToOne = hasMemberAnnotation!(T, m, OneToOne);
2946     immutable bool isManyToOne = hasMemberAnnotation!(T, m, ManyToOne);
2947     immutable bool isManyToMany = hasMemberAnnotation!(T, m, ManyToMany);
2948     immutable bool isOneToMany = hasMemberAnnotation!(T, m, OneToMany);
2949     immutable bool isSimple = isSupportedSimpleType!(T, m);
2950     static if (isSimple) {
2951         return getSimplePropertyDef!(T, m);
2952     } else static if (isObject) {
2953         static if (isOneToOne) {
2954             return getOneToOnePropertyDef!(T, m);
2955         } else static if (isEmbedded) {
2956             return getEmbeddedPropertyDef!(T, m);
2957         } else {
2958             // if no annotations on Object field, assume it is ManyToOne
2959             return getManyToOnePropertyDef!(T, m);
2960         }
2961 
2962     } else static if (isCollection) {
2963         static assert(!isEmbedded && !isOneToOne && !isManyToOne, "Collection object array or LazyCollection! cannot be marked as @Embedded, @OneToOne, or @ManyToOne");
2964         static if (isManyToMany) {
2965             return getManyToManyPropertyDef!(T, m);
2966         } else {
2967             // if no annotations on collection field, assume it is OneToMany
2968             return getOneToManyPropertyDef!(T, m);
2969         }
2970     }
2971 }
2972 
2973 string getEntityDef(T)() {
2974     string res;
2975     string generatedGettersSetters;
2976 
2977     string generatedEntityInfo;
2978     string generatedPropertyInfo;
2979 
2980     immutable string typeName = fullyQualifiedName!T;
2981     immutable bool isEntity = hasAnnotation!(T, Entity);
2982 
2983     //Don't require class level annotation. If no @Embeddable annotation, will treat as if there is @Entity annotation
2984     //static assert (hasOneOfAnnotations!(T, Entity, Embeddable), "Type " ~ typeName ~ " has neither @Entity nor @Embeddable annotation");
2985     static assert (!hasAnnotation!(T, Entity) || !hasAnnotation!(T, Embeddable), "Type " ~ typeName ~ " may not have both @Entity and @Embeddable at the same time");
2986     //pragma(msg, "Entity type name: " ~ typeName);
2987 
2988     immutable string entityName = getEntityName!T();
2989     immutable string tableName = getTableName!T();
2990 
2991     //pragma(msg, "preparing entity : " ~ entityName);
2992 
2993     static assert (entityName != null, "Type " ~ typeName ~ " has no Entity name specified");
2994     static assert (tableName != null, "Type " ~ typeName ~ " has no Table name specified");
2995 
2996     generatedEntityInfo ~= "new EntityInfo(";
2997     generatedEntityInfo ~= "\"" ~ entityName ~ "\", ";
2998     generatedEntityInfo ~= "\"" ~ tableName ~ "\", ";
2999     generatedEntityInfo ~= hasAnnotation!(T, Embeddable) ? "true," : "false,";
3000     generatedEntityInfo ~= "[\n";
3001 
3002     //pragma(msg, entityName ~ " : " ~ ((hasHibernatedEmbeddableAnnotation!T) ? "true," : "false,"));
3003 
3004     foreach (m; __traits(allMembers, T)) {
3005         //pragma(msg, m);
3006 
3007         static if (__traits(compiles, (typeof(__traits(getMember, T, m))))){
3008 
3009             // skip non-public members
3010             static if (__traits(getProtection, __traits(getMember, T, m)) == "public") {
3011 
3012                 alias ti = typeof(__traits(getMember, T, m));
3013 
3014                 // hasHibernatedPropertyAnnotation!(T, m) &&
3015                 // automatically treat all public members of supported types as persistent
3016                 immutable bool typeSupported = (isSupportedSimpleType!(T, m) || isObjectMember!(T, m) || isCollectionMember!(T, m));
3017                 immutable bool isMainProp = isMainMemberForProperty!(T,m) && !hasMemberAnnotation!(T, m, Transient);
3018                 //pragma( msg, entityName ~ ":" ~ tableName ~ "." ~ m ~ ": typeSupported: " ~ (typeSupported ? "true" : "false") ~ " isMainProp: " ~ (isMainProp ? "true" : "false") )
3019                 static if (typeSupported && isMainProp) {
3020                     
3021                     immutable string propertyDef = getPropertyDef!(T, m)();
3022                     //pragma(msg, propertyDef);
3023 
3024                     if (generatedPropertyInfo != null)
3025                         generatedPropertyInfo ~= ",\n";
3026                     generatedPropertyInfo ~= propertyDef;
3027                 }
3028             }
3029         }
3030     }
3031     //pragma(msg, t);
3032     //pragma(msg, typeof(t));
3033 
3034     generatedEntityInfo ~= generatedPropertyInfo;
3035     generatedEntityInfo ~= "],";
3036     generatedEntityInfo ~= "" ~ typeName ~ ".classinfo";
3037     generatedEntityInfo ~= ")";
3038 
3039     //pragma(msg, "built entity : " ~ entityName);
3040 
3041     return generatedEntityInfo ~ "\n" ~ generatedGettersSetters;
3042 }
3043 
3044 template myPackageNamePrefix(alias T)
3045 {
3046     static if (is(typeof(__traits(parent, T))))
3047         enum parent = myPackageNamePrefix!(__traits(parent, T));
3048     else
3049         enum string parent = null;
3050 
3051     static if (T.stringof.startsWith("package "))
3052         enum myPackageNamePrefix = (parent ? parent ~ '.' : "") ~ T.stringof[8 .. $] ~ ".";
3053     else static if (parent)
3054         enum myPackageNamePrefix = parent;
3055     else
3056         enum myPackageNamePrefix = "";
3057 }
3058 
3059 string generateImportFor(T)() {
3060     static if (T.stringof.startsWith("module ")) {
3061         return "import " ~ fullyQualifiedName!T ~ ";\n";
3062     } else {
3063         return "import " ~ myPackageNamePrefix!T ~ moduleName!T ~ ";\n";
3064     }
3065 }
3066 
3067 string entityListDef(T ...)() {
3068     string res;
3069     string imp;
3070     foreach(t; T) {
3071         string impcode = "";
3072         static if (t.stringof.startsWith("module ")) {
3073             impcode = "import " ~ fullyQualifiedName!t ~ ";\n";
3074         } else {
3075             impcode = generateImportFor!(t);
3076         }
3077         if (indexOf(imp, impcode) < 0)
3078             imp ~= impcode;
3079     }
3080     foreach(t; T) {
3081         //pragma(msg, t);
3082         static if (t.stringof.startsWith("module ")) {
3083             //pragma(msg, "is module");
3084             //pragma(msg, "Module passed as schema parameter: " ~ t.stringof);
3085             //pragma(msg, __traits(allMembers, t));
3086             foreach(tt; __traits(allMembers, t)) {
3087                 //alias  ti;
3088                 //pragma(msg, "Module member: " ~ (__traits(getMember, t, tt)).stringof);
3089                 static if (__traits(compiles, isImplicitlyConvertible!((__traits(getMember, t, tt)), Object)) && isImplicitlyConvertible!((__traits(getMember, t, tt)), Object)) {
3090                     //pragma(msg, "checking member" ~ (__traits(getMember, t, tt)).stringof);
3091                     // import class metadata if class or any of its members has hibenrated annotation
3092                     static if (hasHibernatedClassOrPropertyAnnotation!(__traits(getMember, t, tt))) {
3093                         // class should not be marked as @Transient
3094                         static if (!hasAnnotation!(__traits(getMember, t, tt), Transient)) {
3095                             immutable string def = getEntityDef!(__traits(getMember, t, tt));
3096                             if (res.length > 0)
3097                                 res ~= ",\n";
3098                             res ~= def;
3099                         }
3100                     }
3101                 }
3102             }
3103         } else {
3104             //pragma(msg, "not module");
3105             static if (__traits(compiles, isImplicitlyConvertible!(t, Object)) && isImplicitlyConvertible!(t, Object)) {
3106 
3107                 static assert(!hasAnnotation!(t, Transient), "Class " ~ t.stringof ~ " has @Transient annotation and cannot be used in metadata");
3108 
3109                 // will be considered as @Entity if doesn't have @Embeddable annotation
3110                 immutable string def = getEntityDef!t;
3111 
3112                 //pragma(msg, def);
3113 
3114                 if (res.length > 0)
3115                     res ~= ",\n";
3116                 res ~= def;
3117             } else {
3118                     static assert(t.stringof ~ " cannot be passed as schema item");
3119             }
3120         }
3121     }
3122     string code = 
3123         "shared static this() {\n" ~
3124         imp ~ // imports
3125         "    //writeln(\"starting static initializer\");\n" ~
3126         "    entities = [\n" ~ res ~ "];\n" ~
3127         "    EntityInfo [string] map;\n" ~
3128         "    EntityInfo [TypeInfo_Class] typemap;\n" ~
3129         "    foreach(e; entities) {\n" ~
3130         "        map[e.name] = e;\n" ~
3131         "        typemap[cast(TypeInfo_Class)e.classInfo] = e;\n" ~
3132         "    }\n" ~
3133         "    entityMap = map;\n" ~
3134         "    classMap = typemap;\n" ~
3135         "    //writeln(\"updating referenced entities\");\n" ~
3136         "    foreach(e; entities) {\n" ~
3137 		"        //writefln( \"Entity:%s table:%s type:%s\", e.name, e.tableName, e.classInfo.name );\n" ~
3138         "        foreach(p; e._properties) {\n" ~
3139 		"            //writefln( \"\tproperty:%s column:%s ref-entityname:%s ref-propertyname:%s \", p.propertyName, p.columnName, p.referencedEntityName, p.referencedPropertyName );\n" ~
3140         "            if (p.referencedEntityName !is null) {\n" ~
3141         "                //writeln(\"embedded entity \" ~ p.referencedEntityName);\n" ~
3142         "                enforceHelper!MappingException((p.referencedEntityName in map) !is null, \"referenced entity not found in schema: \" ~ p.referencedEntityName);\n" ~
3143         "                p._referencedEntity = map[p.referencedEntityName];\n" ~
3144         "                if (p.referencedPropertyName !is null) {\n" ~
3145         "                    //writeln(\"\t\tembedded entity property name \" ~ p.referencedPropertyName );\n" ~
3146         "                    //writefln(\"\t\tembedded entity._propertyMap: %s \", p._referencedEntity._propertyMap );\n" ~
3147         "                    enforceHelper!MappingException((p.referencedPropertyName in p._referencedEntity._propertyMap) !is null, \"embedded entity property not found in schema: \" ~ p.referencedEntityName);\n" ~
3148         "                    p._referencedProperty = p._referencedEntity._propertyMap[p.referencedPropertyName];\n" ~
3149         "                }\n" ~
3150         "            }\n" ~
3151         "        }\n" ~
3152         "    }\n" ~
3153         "    //writeln(\"finished static initializer\");\n" ~
3154         "}";
3155     //pragma(msg, "built entity list");
3156     return code;
3157 }
3158 
3159 abstract class SchemaInfo : EntityMetaData {
3160 
3161     override @property size_t length() const {
3162         return getEntityCount();
3163     }
3164     override const(EntityInfo) opIndex(int index) const {
3165         return getEntity(index);
3166     }
3167     override const(EntityInfo) opIndex(string entityName) const {
3168         return findEntity(entityName);
3169     }
3170 
3171     override const(PropertyInfo) opIndex(string entityName, string propertyName) const {
3172         return findEntity(entityName).findProperty(propertyName);
3173     }
3174 
3175     override public Variant getPropertyValue(Object obj, string propertyName) const {
3176         return findEntityForObject(obj).getPropertyValue(obj, propertyName);
3177     }
3178 
3179     override public void setPropertyValue(Object obj, string propertyName, Variant value) const {
3180         findEntityForObject(obj).setPropertyValue(obj, propertyName, value);
3181     }
3182 
3183     private void appendCommaDelimitedList(ref string buf, string data) const {
3184         if (buf.length != 0)
3185             buf ~= ", ";
3186         buf ~= data;
3187     }
3188 
3189     public string getAllFieldListForUpdate(Dialect dialect, const EntityInfo ei, bool exceptKey = false) const {
3190         string query;
3191         foreach(pi; ei) {
3192             if (pi.key && exceptKey)
3193                 continue;
3194             if (pi.embedded) {
3195                 auto emei = pi.referencedEntity;
3196                 appendCommaDelimitedList(query, getAllFieldListForUpdate(dialect, emei, exceptKey));
3197             } else if (pi.oneToOne || pi.manyToOne) {
3198                 if (pi.columnName != null) {
3199                     // read FK column
3200                     appendCommaDelimitedList(query, dialect.quoteIfNeeded(pi.columnName) ~ "=?");
3201                 }
3202             } else if (pi.oneToMany || pi.manyToMany) {
3203                 // skip
3204             } else {
3205                 appendCommaDelimitedList(query, dialect.quoteIfNeeded(pi.columnName) ~ "=?");
3206             }
3207         }
3208         return query;
3209     }
3210     
3211     override public string getAllFieldList(Dialect dialect, const EntityInfo ei, bool exceptKey = false) const {
3212         string query;
3213         foreach(pi; ei) {
3214             if (pi.key && exceptKey)
3215                 continue;
3216             if (pi.embedded) {
3217                 auto emei = pi.referencedEntity;
3218                 appendCommaDelimitedList(query, getAllFieldList(dialect, emei, exceptKey));
3219             } else if (pi.oneToOne || pi.manyToOne) {
3220                 if (pi.columnName != null) {
3221                     // read FK column
3222                     appendCommaDelimitedList(query, dialect.quoteIfNeeded(pi.columnName));
3223                 }
3224             } else if (pi.oneToMany || pi.manyToMany) {
3225                 // skip
3226             } else {
3227                 appendCommaDelimitedList(query, dialect.quoteIfNeeded(pi.columnName));
3228             }
3229         }
3230         return query;
3231     }
3232     
3233     override public int getFieldCount(const EntityInfo ei, bool exceptKey) const {
3234         int count = 0;
3235         foreach(pi; ei) {
3236             if (pi.key && exceptKey)
3237                 continue;
3238             if (pi.embedded) {
3239                 auto emei = pi.referencedEntity;
3240                 count += getFieldCount(emei, exceptKey);
3241             } else if (pi.oneToOne || pi.manyToOne) {
3242                 if (pi.columnName != null) {
3243                     // read FK column
3244                     count++;
3245                 }
3246             } else if (pi.oneToMany || pi.manyToMany) {
3247                 // skip
3248             } else {
3249                 count++;
3250             }
3251         }
3252         return count;
3253     }
3254     
3255     public string getAllFieldPlaceholderList(const EntityInfo ei, bool exceptKey = false) const {
3256         string query;
3257         foreach(pi; ei) {
3258             if (pi.key && exceptKey)
3259                 continue;
3260             if (pi.embedded) {
3261                 auto emei = pi.referencedEntity;
3262                 appendCommaDelimitedList(query, getAllFieldPlaceholderList(emei));
3263             } else if (pi.oneToOne || pi.manyToOne) {
3264                 if (pi.columnName != null) {
3265                     // read FK column
3266                     appendCommaDelimitedList(query, "?");
3267                 }
3268             } else if (pi.oneToMany || pi.manyToMany) {
3269                 // skip
3270             } else {
3271                 appendCommaDelimitedList(query, "?");
3272             }
3273         }
3274         return query;
3275     }
3276     
3277     override public string getAllFieldList(Dialect dialect, string entityName, bool exceptKey) const {
3278         return getAllFieldList(dialect, findEntity(entityName), exceptKey);
3279     }
3280 
3281     override public int readAllColumns(Object obj, DataSetReader r, int startColumn) const {
3282         auto ei = findEntityForObject(obj);
3283         int columnCount = 0;
3284         foreach(pi; ei) {
3285             if (pi.embedded) {
3286                 auto emei = pi.referencedEntity;
3287                 Object em = emei.createEntity();
3288                 int columnsRead = readAllColumns(em, r, startColumn + columnCount);
3289                 pi.setObjectFunc(obj, em);
3290                 columnCount += columnsRead;
3291             } else if (pi.oneToOne || pi.manyToOne) {
3292                 if (pi.columnName !is null) {
3293                     Variant fk = r.getVariant(startColumn + columnCount);
3294                     // TODO: use FK
3295                     columnCount++;
3296                 } else {
3297                     // TODO: plan reading
3298                 }
3299             } else if (pi.oneToMany || pi.manyToMany) {
3300                 // skip
3301             } else {
3302                 pi.readFunc(obj, r, startColumn + columnCount);
3303                 columnCount++;
3304             }
3305         }
3306         return columnCount;
3307     }
3308 
3309     override public int writeAllColumns(Object obj, DataSetWriter w, int startColumn, bool exceptKey = false) const {
3310         auto ei = findEntityForObject(obj);
3311         //writeln(ei.name ~ ".writeAllColumns");
3312         int columnCount = 0;
3313         foreach(pi; ei) {
3314             if (pi.key && exceptKey)
3315                 continue;
3316             if (pi.embedded) {
3317                 auto emei = pi.referencedEntity;
3318                 //writeln("getting embedded entity " ~ emei.name);
3319                 assert(pi.getObjectFunc !is null, "No getObjectFunc defined for embedded entity " ~ emei.name);
3320                 Object em = pi.getObjectFunc(obj);
3321                 if (em is null)
3322                     em = emei.createEntity();
3323                 assert(em !is null, "embedded object is null");
3324                 //writeln("writing embedded entity " ~ emei.name);
3325                 int columnsWritten = writeAllColumns(em, w, startColumn + columnCount);
3326                 //writeln("written");
3327                 columnCount += columnsWritten;
3328             } else if (pi.oneToOne || pi.manyToOne) {
3329                 if (pi.columnName !is null) {
3330                     Object objFunc = pi.getObjectFunc(obj);
3331                     if (objFunc is null) {
3332                         w.setNull(startColumn + columnCount);
3333                     } else {
3334                         //writeln("setting ID column for property " ~ pi.entity.name ~ "." ~ pi.propertyName);
3335                         //if (pi.lazyLoad)
3336                         //    writeln("property has lazy loader");
3337                         //writeln("reading ID variant " ~ pi.propertyName ~ " from object");
3338                         Variant id = pi.referencedEntity.getKey(objFunc);
3339                         //writeln("setting parameter " ~ to!string(startColumn + columnCount));
3340                         w.setVariant(startColumn + columnCount, id);
3341                     }
3342                     columnCount++;
3343                 }
3344                 // skip
3345             } else if (pi.oneToMany || pi.manyToMany) {
3346                 // skip
3347             } else {
3348                 pi.writeFunc(obj, w, startColumn + columnCount);
3349                 columnCount++;
3350             }
3351         }
3352         return columnCount;
3353     }
3354 
3355     override public string generateFindAllForEntity(Dialect dialect, string entityName) const {
3356         auto ei = findEntity(entityName);
3357         return "SELECT " ~ getAllFieldList(dialect, ei) ~ " FROM " ~ dialect.quoteIfNeeded(ei.tableName);
3358     }
3359 
3360     override public string generateFindByPkForEntity(Dialect dialect, const EntityInfo ei) const {
3361         return "SELECT " ~ getAllFieldList(dialect, ei) ~ " FROM " ~ dialect.quoteIfNeeded(ei.tableName) ~ " WHERE " ~ dialect.quoteIfNeeded(ei.keyProperty.columnName) ~ " = ?";
3362     }
3363 
3364     override public string generateInsertAllFieldsForEntity(Dialect dialect, const EntityInfo ei) const {
3365         return "INSERT INTO " ~ dialect.quoteIfNeeded(ei.tableName) ~ "(" ~ getAllFieldList(dialect, ei) ~ ") VALUES (" ~ getAllFieldPlaceholderList(ei) ~ ")";
3366     }
3367 
3368     override public string generateInsertNoKeyForEntity(Dialect dialect, const EntityInfo ei) const {
3369         return "INSERT INTO " ~ dialect.quoteIfNeeded(ei.tableName) ~ "(" ~ getAllFieldList(dialect, ei, true) ~ ") VALUES (" ~ getAllFieldPlaceholderList(ei, true) ~ ")";
3370     }
3371 
3372     override public string generateUpdateForEntity(Dialect dialect, const EntityInfo ei) const {
3373         return "UPDATE " ~ dialect.quoteIfNeeded(ei.tableName) ~ " SET " ~ getAllFieldListForUpdate(dialect, ei, true) ~ " WHERE " ~ dialect.quoteIfNeeded(ei.getKeyProperty().columnName) ~ "=?";
3374     }
3375 
3376     override public string generateFindByPkForEntity(Dialect dialect, string entityName) const {
3377         return generateFindByPkForEntity(dialect, findEntity(entityName));
3378     }
3379 
3380     override public string generateInsertAllFieldsForEntity(Dialect dialect, string entityName) const {
3381         return generateInsertAllFieldsForEntity(dialect, findEntity(entityName));
3382     }
3383 }
3384 
3385 class SchemaInfoImpl(T...) : SchemaInfo {
3386     __gshared EntityInfo [string] entityMap;
3387     __gshared EntityInfo [] entities;
3388     __gshared EntityInfo [TypeInfo_Class] classMap;
3389 
3390     //import htestmain;
3391     //pragma(msg, entityListDef!(T)());
3392     mixin(entityListDef!(T)());
3393 
3394     override public int getEntityCount() const { return cast(int)entities.length; }
3395 
3396     override public const(EntityInfo[]) getEntities() const  { return entities; }
3397     override public const(EntityInfo[string]) getEntityMap() const  { return entityMap; }
3398     override public const(EntityInfo [TypeInfo_Class]) getClassMap() const  { return classMap; }
3399 
3400     override int opApply(int delegate(ref const EntityInfo) dg) const { 
3401         int result = 0; 
3402         for (int i = 0; i < entities.length; i++) { 
3403             result = dg(entities[i]); 
3404             if (result) break; 
3405         } 
3406         return result; 
3407     }
3408 
3409     override public const(EntityInfo) findEntity(string entityName) const  {
3410         enforceHelper!MappingException((entityName in entityMap) !is null, "Cannot find entity by name " ~ entityName);
3411         return entityMap[entityName]; 
3412     }
3413 
3414     override public const(EntityInfo) findEntity(TypeInfo_Class entityClass) const { 
3415         enforceHelper!MappingException((entityClass in classMap) !is null, "Cannot find entity by class " ~ entityClass.toString());
3416         return classMap[entityClass]; 
3417     }
3418 
3419     override public const(EntityInfo) getEntity(int entityIndex) const { 
3420         enforceHelper!MappingException(entityIndex >= 0 && entityIndex < entities.length, "Invalid entity index " ~ to!string(entityIndex));
3421         return entities[entityIndex]; 
3422     }
3423 
3424     override public Object createEntity(string entityName) const { 
3425         enforceHelper!MappingException((entityName in entityMap) !is null, "Cannot find entity by name " ~ entityName);
3426         return entityMap[entityName].createEntity(); 
3427     }
3428 
3429     override public const(EntityInfo) findEntityForObject(Object obj) const {
3430         enforceHelper!MappingException((obj.classinfo in classMap) !is null, "Cannot find entity by class " ~ obj.classinfo.toString());
3431         return classMap[obj.classinfo];
3432     }
3433     this() {
3434         // update entity._metadata reference
3435         foreach(e; entities) {
3436             e._metadata = this;
3437             int columnOffset = 0;
3438             foreach(p; e._properties) {
3439                 if (p.manyToMany) {
3440                     p.updateJoinTable();
3441                 }
3442                 p._columnOffset = columnOffset;
3443                 if (p.embedded) {
3444                     auto emei = p.referencedEntity;
3445                     columnOffset += e.metadata.getFieldCount(emei, false);
3446                 } else if (p.oneToOne || p.manyToOne) {
3447                     if (p.columnName != null) {
3448                         // read FK column
3449                         columnOffset++;
3450                     }
3451                 } else if( p.manyToMany || p.oneToMany ) {
3452                     //manyToMany and oneToMany do NOT have a column in the table.
3453                 } else {
3454                     columnOffset++;
3455                 }
3456             }
3457         }
3458     }
3459 }
3460 
3461 /// information about DB structure generated from HibernateD entity metadata
3462 class DBInfo {
3463     Dialect dialect;
3464     EntityMetaData metaData;
3465     bool hasCircularRefs;
3466 
3467     this(Dialect dialect, EntityMetaData metaData) {
3468         this.dialect = dialect;
3469         this.metaData = metaData;
3470 
3471         foreach(entity; metaData) {
3472             if (!entity.embeddable)
3473                 add(new TableInfo(this, entity));
3474         }
3475         sortTables();
3476     }
3477 
3478     TableInfo[] tables;
3479     TableInfo[string] tableNameMap;
3480     TableInfo get(string tableName) {
3481         TableInfo res = find(tableName);
3482         enforceHelper!HibernatedException(res !is null, "table " ~ tableName ~ " is not found in schema");
3483         return res;
3484     }
3485     TableInfo find(string tableName) {
3486         if ((tableName in tableNameMap) is null)
3487             return null;
3488         return tableNameMap[tableName];
3489     }
3490     void add(TableInfo table) {
3491         enforceHelper!HibernatedException((table.tableName in tableNameMap) is null, "duplicate table " ~ table.tableName ~ " in schema");
3492         tables ~= table;
3493         tableNameMap[table.tableName] = table;
3494     }
3495     private static bool[string] arrayToMap(string[] keys) {
3496         bool[string] res;
3497         if (keys !is null) {
3498             foreach(key; keys)
3499                 res[key] = true;
3500         }
3501         return res;
3502     }
3503 
3504     /// drop and/or create tables and indexes in DB using specified connection
3505     void updateDBSchema(Connection conn, bool dropTables, bool createTables) {
3506         assert(dropTables || createTables);
3507         string[] existingTables = getExistingTables(conn);
3508         string[] batch;
3509         if (dropTables)
3510             batch ~= getDropTableSQL(existingTables);
3511         if (createTables)
3512             batch ~= getCreateTableSQL(dropTables ? null : existingTables);
3513         try {
3514             Statement stmt = conn.createStatement();
3515             scope(exit) stmt.close();
3516             foreach(sql; batch) {
3517                 stmt.executeUpdate(sql);
3518             }
3519         } catch (Throwable e) {
3520             throw new HibernatedException(e);
3521         }
3522     }
3523 
3524     string[] getExistingTables(Connection conn) {
3525         string[] res;
3526         try {
3527             Statement stmt = conn.createStatement();
3528             scope(exit) stmt.close();
3529             foreach(table; tables) {
3530                 string sql = dialect.getCheckTableExistsSQL(table.tableName);
3531                 ResultSet rs = stmt.executeQuery(sql);
3532                 scope(exit)rs.close();
3533                 if (rs.next())
3534                     res ~= table.tableName;
3535             }
3536         } catch (Throwable e) {
3537             throw new HibernatedException(e);
3538         }
3539         return res;
3540     }
3541     string[] getCreateTableSQL(string[] existingTables = null) {
3542         auto map = arrayToMap(existingTables);
3543         string[] res;
3544         foreach(table; tables) {
3545             if (existingTables is null || (table.tableName in map) is null)
3546                 res ~= table.getCreateTableSQL();
3547         }
3548         return res;
3549     }
3550     string[] getCreateIndexSQL(string[] existingTables = null) {
3551         auto map = arrayToMap(existingTables);
3552         string[] res;
3553         foreach(table; tables) {
3554             if (existingTables is null || (table.tableName in map) is null)
3555                 res ~= table.getCreateIndexSQL();
3556         }
3557         return res;
3558     }
3559     string[] getDropTableSQL(string[] existingTables = null) {
3560         auto map = arrayToMap(existingTables);
3561         string[] res;
3562         foreach(table; tables) {
3563             if (existingTables is null || (table.tableName in map) !is null) {
3564                 if (hasCircularRefs)
3565                     res ~= table.getDropIndexSQL();
3566                 res ~= table.getDropTableSQL();
3567             }
3568         }
3569         return res;
3570     }
3571     TableInfo opIndex(string tableName) {
3572         TableInfo ti = find(tableName);
3573         enforceHelper!HibernatedException(ti !is null, "Table " ~ tableName ~ " is not found in schema");
3574         return ti;
3575     }
3576     private static TableInfo[] addTableSorted(TableInfo[] list, TableInfo table) {
3577         TableInfo[] head;
3578         TableInfo[] tail;
3579         if (list.length == 0) {
3580             // trivial
3581             return [table];
3582         } else {
3583             foreach(ti; list) {
3584                 if (ti.references(table))
3585                     tail ~= ti;
3586                 else
3587                     head ~= ti;
3588             }
3589             return head ~ [table] ~ tail;
3590         }
3591     }
3592     private void sortTables() {
3593         TableInfo[] list;
3594         foreach(table; tables) {
3595             list = addTableSorted(list, table);
3596         }
3597         tables = list;
3598         hasCircularRefs = hasCircularReferences();
3599         if (hasCircularRefs)
3600             writeln("has circular references");
3601     }
3602     private bool hasCircularReferences() {
3603         for (int i=0; i<tables.length; i++)
3604             for (int j=i + 1; j<tables.length; j++)
3605                 if (tables[i].references(tables[j]))
3606                     return true;
3607         return false;
3608     }
3609 }
3610 
3611 /// information about table in DB
3612 class TableInfo {
3613     DBInfo schema;
3614     string tableName;
3615     const EntityInfo entity;
3616     const EntityInfo entity2;
3617     ColumnInfo[] columns;
3618     ColumnInfo[string] columnNameMap;
3619     IndexInfo[] indexes;
3620     const string pkDef;
3621 
3622     this(DBInfo schema, const EntityInfo entity, const EntityInfo entity2, const JoinTableInfo joinTable) {
3623         this.schema = schema;
3624         this.tableName = joinTable.tableName;
3625         this.entity = entity;
3626         this.entity2 = entity;
3627         ColumnInfo c1;
3628         ColumnInfo c2;
3629         assert(joinTable.column1 !is null);
3630         assert(joinTable.column2 !is null);
3631         assert(entity !is null);
3632         assert(entity2 !is null);
3633         assert(joinTable.thisEntity !is null);
3634         assert(joinTable.otherEntity !is null);
3635         if (joinTable.column1 < joinTable.column2) {
3636             c1 = new ColumnInfo(this, joinTable.column1, entity);
3637             c2 = new ColumnInfo(this, joinTable.column2, entity2);
3638         } else {
3639             c2 = new ColumnInfo(this, joinTable.column1, entity);
3640             c1 = new ColumnInfo(this, joinTable.column2, entity2);
3641         }
3642         addColumn(c1);
3643         addColumn(c2);
3644         pkDef = "PRIMARY KEY (" ~ schema.dialect.quoteIfNeeded(c1.columnName) ~ ", " ~ schema.dialect.quoteIfNeeded(c2.columnName) ~ "), " ~
3645             schema.dialect.getUniqueIndexItemSQL(tableName ~ "_reverse_index", [c2.columnName, c1.columnName]);
3646         addForeignKey(tableName, entity, joinTable.column1, null);
3647         addForeignKey(tableName, entity2, joinTable.column2, null);
3648     }
3649 
3650     ColumnInfo opIndex(string columnName) {
3651         ColumnInfo ti = find(columnName);
3652         enforceHelper!HibernatedException(ti !is null, "Column " ~ columnName ~ " is not found in table " ~ tableName);
3653         return ti;
3654     }
3655 
3656     ColumnInfo find(string columnName) {
3657         if ((columnName in columnNameMap) is null)
3658             return null;
3659         return columnNameMap[columnName];
3660     }
3661 
3662     private void appendColumns(const EntityInfo entity) {
3663         foreach(pi; entity) {
3664             if (pi.embedded) {
3665                 appendColumns(pi.referencedEntity);
3666             } else if (pi.simple || (pi.columnName !is null)) {
3667                 addColumn(new ColumnInfo(this, pi));
3668                 if (pi.simple && pi.uniqueIndex !is null) //pi.unique)
3669                     addUniqueColumnIndex(pi);
3670             } else if (pi.manyToMany) {
3671                 addJoinTable(pi);
3672             }
3673         }
3674     }
3675     this(DBInfo schema, const EntityInfo entity) {
3676         this.schema = schema;
3677         this.entity = entity;
3678         this.entity2 = null;
3679         this.tableName = entity.tableName;
3680         this.pkDef = null;
3681         appendColumns(entity);
3682     }
3683     void addJoinTable(const PropertyInfo pi) {
3684         assert(pi.referencedEntity !is null);
3685         assert(pi.joinTable !is null);
3686         TableInfo t = new TableInfo(schema, entity, pi.referencedEntity, pi.joinTable);
3687         TableInfo existing = schema.find(t.tableName);
3688         if (existing !is null) {
3689             enforceHelper!HibernatedException(t.getCreateTableSQL() == existing.getCreateTableSQL(), "JoinTable structure in " ~ entity.name ~ " and " ~ pi.referencedEntityName ~ " do not match");
3690         } else {
3691             schema.add(t);
3692         }
3693     }
3694     void addUniqueColumnIndex(const PropertyInfo pi) {
3695         assert(pi.columnName !is null);
3696         IndexInfo index = new IndexInfo(this, IndexType.Unique);
3697         index.indexName = pi.uniqueIndex;
3698         index.columnNames ~= pi.columnName;
3699         addIndex(index);
3700     }
3701     void addForeignKey(string thisTable, const EntityInfo otherEntity, string columnName, string uniqueIndex) {
3702         IndexInfo index = new IndexInfo(this, uniqueIndex is null ? IndexType.ForeignKey : IndexType.UniqueForeignKey);
3703         index.indexName = thisTable ~ "_" ~ columnName ~ "_index";
3704         index.columnNames ~= columnName;
3705         index.referencedTable = otherEntity.tableName;
3706         index.referencedColumnNames ~= otherEntity.getKeyProperty().columnName;
3707         addIndex(index);
3708     }
3709     void addForeignKey(const PropertyInfo pi) {
3710         assert(pi.columnName !is null);
3711         assert(pi.manyToOne || pi.oneToOne);
3712         addForeignKey(pi.entity.tableName, pi.referencedEntity, pi.columnName, pi.uniqueIndex);
3713     }
3714     private void addIndex(IndexInfo index) {
3715         // TODO: check duplicates
3716         indexes ~= index;
3717     }
3718     void addColumn(ColumnInfo column) {
3719         enforceHelper!HibernatedException((column.columnName in columnNameMap) is null, "duplicate column name " ~ tableName ~ "." ~ column.columnName ~ " in schema");
3720         columns ~= column;
3721         columnNameMap[column.columnName] = column;
3722         if (column.property !is null && (column.property.manyToOne || column.property.oneToOne)) {
3723             addForeignKey(column.property);
3724         }
3725     }
3726     string getCreateTableSQL() {
3727         string res;
3728         foreach(col; columns) {
3729             if (res.length > 0)
3730                 res ~= ", ";
3731             res ~= col.columnDefinition;
3732         }
3733         if (pkDef !is null)
3734             res ~= ", " ~ pkDef;
3735         return "CREATE TABLE " ~ schema.dialect.quoteIfNeeded(tableName) ~ " (" ~ res ~ ")";
3736     }
3737     string getDropTableSQL() {
3738         return "DROP TABLE IF EXISTS " ~ schema.dialect.quoteIfNeeded(tableName);
3739     }
3740     string[] getDropIndexSQL() {
3741         string[] res;
3742         foreach(index; indexes) {
3743             if (index.type == IndexType.ForeignKey || index.type == IndexType.UniqueForeignKey) {
3744                 res ~= index.getDropIndexSQL();
3745             }
3746         }
3747         return res;
3748     }
3749     string[] getCreateIndexSQL() {
3750         string[] res;
3751         foreach(index; indexes) {
3752             res ~= index.getCreateIndexSQL();
3753         }
3754         return res;
3755     }
3756     bool references(ref bool[string] visitedTables, TableInfo other) {
3757         visitedTables[tableName] = true;
3758         foreach(index; indexes) {
3759             if (index.type == IndexType.ForeignKey || index.type == IndexType.UniqueForeignKey) {
3760                 if (index.referencedTable == other.tableName)
3761                     return true;
3762                 if ((index.referencedTable in visitedTables) is null) {
3763                     // not yet visited
3764                     TableInfo t = schema.find(index.referencedTable);
3765                     enforceHelper!HibernatedException(t !is null, "Table " ~ index.referencedTable ~ " referenced in index " ~ index.indexName ~ " is not found in schema");
3766                     if (t.references(visitedTables, other))
3767                         return true;
3768                 }
3769             }
3770         }
3771         return false;
3772     }
3773     bool references(TableInfo other) {
3774         bool[string] visitedTables;
3775         return references(visitedTables, other);
3776     }
3777 }
3778 
3779 class ColumnInfo {
3780     TableInfo table;
3781     const PropertyInfo property;
3782     string columnName;
3783     string columnDefinition;
3784     this(TableInfo table, string columnName, const EntityInfo referencedEntity) {
3785         this.table = table;
3786         this.property = null;
3787         this.columnName = columnName;
3788         this.columnDefinition = table.schema.dialect.quoteIfNeeded(columnName) ~ " " ~ 
3789                 table.schema.dialect.getColumnTypeDefinition(null, referencedEntity.getKeyProperty());
3790     }
3791     this(TableInfo table, const PropertyInfo property) {
3792         this.table = table;
3793         this.property = property;
3794         this.columnName = property.columnName;
3795         assert(columnName !is null);
3796         if (property.manyToOne || property.oneToOne) {
3797             assert(property.columnName !is null);
3798             assert(property.referencedEntity !is null);
3799             this.columnDefinition = table.schema.dialect.quoteIfNeeded(property.columnName) ~ " " ~ table.schema.dialect.getColumnTypeDefinition(property, property.referencedEntity.getKeyProperty());
3800         } else {
3801             this.columnDefinition = table.schema.dialect.getColumnDefinition(property);
3802         }
3803     }
3804 }
3805 
3806 enum IndexType {
3807     Index,
3808     Unique,
3809     ForeignKey,
3810     UniqueForeignKey
3811 }
3812 
3813 class IndexInfo {
3814     TableInfo table;
3815     IndexType type;
3816     string indexName;
3817     string[] columnNames;
3818     string referencedTable;
3819     string[] referencedColumnNames;
3820     this(TableInfo table, IndexType type) {
3821         this.table = table;
3822         this.type = type;
3823     }
3824     string[] getDropIndexSQL() {
3825         final switch(type) {
3826             case IndexType.Unique:
3827             case IndexType.Index:
3828                 return [table.schema.dialect.getDropIndexSQL(table.tableName, indexName)];
3829             case IndexType.ForeignKey:
3830             case IndexType.UniqueForeignKey:
3831                 return [table.schema.dialect.getDropForeignKeySQL(table.tableName, indexName), 
3832                         table.schema.dialect.getDropIndexSQL(table.tableName, indexName)];
3833         }
3834     }
3835     string[] getCreateIndexSQL() {
3836         final switch(type) {
3837             case IndexType.Unique:
3838                 return [table.schema.dialect.getUniqueIndexSQL(table.tableName, indexName, columnNames)];
3839             case IndexType.Index:
3840                 return [table.schema.dialect.getIndexSQL(table.tableName, indexName, columnNames)];
3841             case IndexType.ForeignKey:
3842                 return [table.schema.dialect.getForeignKeySQL(table.tableName, indexName, columnNames, referencedTable, referencedColumnNames)];
3843             case IndexType.UniqueForeignKey:
3844                 return [table.schema.dialect.getUniqueIndexSQL(table.tableName, indexName, columnNames), 
3845                         table.schema.dialect.getForeignKeySQL(table.tableName, indexName, columnNames, referencedTable, referencedColumnNames)];
3846         }
3847     }
3848 }
3849 
3850 unittest {
3851 
3852     @Entity
3853     @Table("users")
3854     static class User {
3855         
3856         //@Id @Generated
3857         @Column("id_column")
3858         int id;
3859         
3860         @Column("name_column")
3861         string name;
3862         
3863         // no column name
3864         //@Column
3865         string flags;
3866         
3867         // annotated getter
3868         private string login;
3869         //@Column
3870         public string getLogin() { return login; }
3871         public void setLogin(string login) { this.login = login; }
3872         
3873         // no (), no column name
3874         //@Column
3875         int testColumn;
3876     }
3877     
3878     
3879     @Entity
3880     @Table("customer")
3881     static class Customer {
3882         //@Id @Generated
3883         //@Column
3884         int id;
3885         //@Column
3886         string name;
3887     }
3888 
3889 
3890     EntityInfo entity = new EntityInfo("user", "users",  false, [
3891                                                                  new PropertyInfo("id", "id", new NumberType(10,false,SqlType.INTEGER), 0, true, true, false, null, RelationType.None, null, null, null, null, null, null, null, null, null)
3892                                                          ], null);
3893 
3894     assert(entity.properties.length == 1);
3895 
3896 
3897 //  immutable string info = getEntityDef!User();
3898 //  immutable string infos = entityListDef!(User, Customer)();
3899 
3900     EntityInfo ei = new EntityInfo("User", "users", false, [
3901                                                             new PropertyInfo("id", "id_column", new NumberType(10,false,SqlType.INTEGER), 0, true, true, false, null, RelationType.None, null, null, null, null, null, null, null, null, null),
3902                                                             new PropertyInfo("name", "name_column", new StringType(), 0, false, false, false, null, RelationType.None, null, null, null, null, null, null, null, null, null),
3903                                                             new PropertyInfo("flags", "flags", new StringType(), 0, false, false, true, null, RelationType.None, null, null, null, null, null, null, null, null, null),
3904                                                             new PropertyInfo("login", "login", new StringType(), 0, false, false, true, null, RelationType.None, null, null, null, null, null, null, null, null, null),
3905                                                             new PropertyInfo("testColumn", "testcolumn", new NumberType(10,false,SqlType.INTEGER), 0, false, false, true, null, RelationType.None, null, null, null, null, null, null, null, null, null)], null);
3906 
3907     //void function(User, DataSetReader, int) readFunc = function(User entity, DataSetReader reader, int index) { };
3908 
3909     assert(ei.findProperty("name").columnName == "name_column");
3910     assert(ei.getProperties()[0].columnName == "id_column");
3911     assert(ei.getProperty(2).propertyName == "flags");
3912     assert(ei.getPropertyCount == 5);
3913 
3914     EntityInfo[] entities3 =  [
3915                                new EntityInfo("User", "users", false, [
3916                                             new PropertyInfo("id", "id_column", new NumberType(10,false,SqlType.INTEGER), 0, true, true, false, null, RelationType.None, null, null, null, null, null, null, null, null, null),
3917                                             new PropertyInfo("name", "name_column", new StringType(), 0, false, false, false, null, RelationType.None, null, null, null, null, null, null, null, null, null),
3918                                             new PropertyInfo("flags", "flags", new StringType(), 0, false, false, true, null, RelationType.None, null, null, null, null, null, null, null, null, null),
3919                                             new PropertyInfo("login", "login", new StringType(), 0, false, false, true, null, RelationType.None, null, null, null, null, null, null, null, null, null),
3920                                             new PropertyInfo("testColumn", "testcolumn", new NumberType(10,false,SqlType.INTEGER), 0, false, false, true, null, RelationType.None, null, null, null, null, null, null, null, null, null)], null)
3921                                                                      ,
3922                                new EntityInfo("Customer", "customer", false, [
3923                                                    new PropertyInfo("id", "id", new NumberType(10,false,SqlType.INTEGER), 0, true, true, true, null, RelationType.None, null, null, null, null, null, null, null, null, null),
3924                                                    new PropertyInfo("name", "name", new StringType(), 0, false, false, true, null, RelationType.None, null, null, null, null, null, null, null, null, null)], null)
3925                                                                      ];
3926 
3927 
3928 }
3929 
3930