1 /**
2  * DStruct - 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 dstruct/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 dstruct.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 dstruct.ddbc.core;
30 import dstruct.ddbc.common;
31 
32 import dstruct.annotations;
33 import dstruct.core;
34 import dstruct.type;
35 import dstruct.session;
36 import dstruct.dialect;
37 import dstruct.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 void function(Object, DataSetReader, int index) ReaderFunc;
113     /// writes simple property value to data set from object
114     alias void function(Object, DataSetWriter, int index) WriterFunc;
115     /// copy property from second passed object to first
116     alias void function(Object, Object) CopyFunc;
117     /// returns simple property as Variant
118     alias Variant function(Object) GetVariantFunc;
119     /// sets simple property from Variant
120     alias void function(Object, Variant value) SetVariantFunc;
121     /// returns true if property value of object is not null
122     alias bool function(Object) IsNullFunc;
123     /// returns true if key property of object is set (similar to IsNullFunc but returns true if non-nullable number is 0.
124     alias bool function(Object) KeyIsSetFunc;
125     /// returns OneToOne, ManyToOne or Embedded property as Object
126     alias Object function(Object) GetObjectFunc;
127     /// sets OneToOne, ManyToOne or Embedded property as Object
128     alias void function(Object, Object) SetObjectFunc;
129     /// sets lazy loader delegate for OneToOne, or ManyToOne property if it's Lazy! template instance
130     alias void function(Object, Object delegate()) SetObjectDelegateFunc;
131     /// sets lazy loader delegate for OneToMany, or ManyToMany property if it's LazyCollection! template instance
132     alias void function(Object, Object[] delegate()) SetCollectionDelegateFunc;
133     /// returns OneToMany or ManyToMany property value as object array
134     alias Object[] function(Object) GetCollectionFunc;
135     /// sets OneToMany or ManyToMany property value from object array
136     alias void function(Object, Object[]) SetCollectionFunc;
137     /// returns true if Lazy! or LazyCollection! property is loaded (no loader delegate set).
138     alias bool function(Object) IsLoadedFunc;
139     /// returns new generated primary key for property
140     alias Variant function(Connection conn, const PropertyInfo prop) GeneratorFunc;
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 hasDStructPropertyAnnotation(T, string m) {
495     enum bool hasDStructPropertyAnnotation = hasOneOfMemberAnnotations!(T, m, Id, Column, OneToOne, ManyToOne, ManyToMany, OneToMany, Generated, Generator);
496 }
497 
498 bool hasDStructClassOrPropertyAnnotation(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 (hasDStructPropertyAnnotation!(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 typeof(__traits(getMember, T, m)) ti;
898     static if (is(ti == function)) {
899         return getterNameToFieldName(m);
900     } else
901         return m;
902 }
903 
904 enum PropertyMemberKind : int {
905     FIELD_MEMBER,      // int field;
906     GETTER_MEMBER,     // getField() + setField() or isField() and setField()
907     PROPERTY_MEMBER,   // @property T field() { ... } + @property xxx field(T value) { ... }
908     LAZY_MEMBER,       // Lazy!Object field;
909     UNSUPPORTED_MEMBER,// 
910 }
911 
912 bool hasPercentSign(immutable string str) {
913     foreach(ch; str) {
914         if (ch == '%')
915             return true;
916     }
917     return false;
918 }
919 
920 int percentSignCount(immutable string str) {
921     string res;
922     foreach(ch; str) {
923         if (ch == '%')
924             res ~= "%";
925     }
926     return cast(int)res.length;
927 }
928 
929 string substituteParam(immutable string fmt, immutable string value) {
930     if (hasPercentSign(fmt))
931         return format(fmt, value);
932     else
933         return fmt;
934 }
935 
936 //string substituteIntParam(immutable string fmt, immutable int value) {
937 //    int percentPos = -1;
938 //    for (int i=0; i<fmt.length; i++) {
939 //        if (fmt[i] == '%') {
940 //            percentPos = i;
941 //        }
942 //
943 //    }
944 //    if (percentPos < 0)
945 //        return fmt;
946 //    return fmt[0 .. percentPos] ~ "1024" ~ fmt[percentPos + 2 .. $]; //to!string(value)
947 ////    string res;
948 ////    bool skipNext = false;
949 ////
950 ////    foreach(ch; fmt) {
951 ////        if (ch == '%') {
952 ////            res ~= "1024"; //to!string(value);
953 ////            skipNext = true;
954 ////        } else if (!skipNext) {
955 ////            res ~= ch;
956 ////            skipNext = false;
957 ////        }
958 ////    }
959 ////    return res;
960 //    // following code causes error in DMD
961 ////    if (hasPercentSign(fmt))
962 ////        return format(fmt, value);
963 ////    else
964 ////        return fmt;
965 //}
966 
967 string substituteParamTwice(immutable string fmt, immutable string value) {
968     immutable int paramCount = cast(int)percentSignCount(fmt);
969     if (paramCount == 1)
970         return format(fmt, value);
971     else if (paramCount == 2)
972         return format(fmt, value, value);
973     else
974         return fmt;
975 }
976 
977 static immutable string[] PropertyMemberKind_ReadCode = 
978     [
979         "entity.%s",
980         "entity.%s()",
981         "entity.%s",
982         "entity.%s()",
983         "dummy"
984     ];
985 
986 PropertyMemberKind getPropertyMemberKind(T : Object, string m)() {
987     auto memberKind = PropertyMemberKind.UNSUPPORTED_MEMBER;
988     alias typeof(__traits(getMember, T, m)) ti;
989     static if (is(ti == function)) {
990         // interate through all overloads
991         //return checkGetterOverload!(T, m);
992         foreach(overload; MemberFunctionsTuple!(T, m)) {
993             static if (ParameterTypeTuple!(overload).length == 0) {
994                 static if (functionAttributes!overload & FunctionAttribute.property) {
995                     memberKind = PropertyMemberKind.PROPERTY_MEMBER;
996                     break;
997                 }
998                 else static if (m.startsWith("get") || m.startsWith("is")) {
999                     memberKind = PropertyMemberKind.GETTER_MEMBER;
1000                     break;
1001                 }
1002             }
1003         }
1004     } else {
1005         static if (isLazyInstance!(ti)) {
1006             memberKind = PropertyMemberKind.LAZY_MEMBER;
1007         } else {
1008             memberKind = PropertyMemberKind.FIELD_MEMBER;
1009         }
1010     }
1011     return memberKind;
1012 }
1013 
1014 string getPropertyEmbeddedEntityName(T : Object, string m)() {
1015     alias typeof(__traits(getMember, T, m)) ti;
1016     static if (is(ti == function)) {
1017         static if (isImplicitlyConvertible!(ReturnType!(ti), Object)) {
1018             static assert(hasAnnotation!(ReturnType!(ti), Embeddable), "@Embedded property class should have @Embeddable annotation");
1019             return getEntityName!(ReturnType!(ti));
1020         } else
1021             static assert(false, "@Embedded property can be only class with @Embeddable annotation");
1022     } else {
1023         static if (isImplicitlyConvertible!(ti, Object)) {
1024             static assert(hasAnnotation!(ti, Embeddable), "@Embedded property class should have @Embeddable annotation");
1025             return getEntityName!ti;
1026         } else 
1027             static assert(false, "@Embedded property can be only class with @Embeddable annotation");
1028     }
1029 }
1030 
1031 template isLazyInstance(T) {
1032     static if (is(T x == Lazy!Args, Args...))
1033         enum bool isLazyInstance = true;
1034     else
1035         enum bool isLazyInstance = false;
1036 }
1037 
1038 template isLazyCollectionInstance(T) {
1039     static if (is(T x == LazyCollection!Args, Args...))
1040         enum bool isLazyCollectionInstance = true;
1041     else
1042         enum bool isLazyCollectionInstance = false;
1043 }
1044 
1045 template isLazyMember(T : Object, string m) {
1046     static if (is(typeof(__traits(getMember, T, m)) x == Lazy!Args, Args...))
1047         enum bool isLazyMember = true;
1048     else
1049         enum bool isLazyMember = false;
1050 }
1051 
1052 template isLazyCollectionMember(T : Object, string m) {
1053     static if (is(typeof(__traits(getMember, T, m)) x == LazyCollection!Args, Args...))
1054         enum bool isLazyCollectionMember = true;
1055     else
1056         enum bool isLazyCollectionMember = false;
1057 }
1058 
1059 template isObject(T) {
1060     enum bool isObject = (__traits(compiles, isImplicitlyConvertible!(T, Object)) && isImplicitlyConvertible!(T, Object));
1061 }
1062 
1063 /// member is field or function or property with SomeClass type
1064 template isObjectMember(T : Object, string m) {
1065     alias typeof(__traits(getMember, T, m)) ti;
1066     static if (is(ti == function)) {
1067         enum bool isObjectMember = isImplicitlyConvertible!(ReturnType!(ti), Object);
1068     } else {
1069         enum bool isObjectMember = isImplicitlyConvertible!(ti, Object);
1070     }
1071 }
1072 
1073 template hasPublicMember(T : Object, string m) {
1074     //pragma(msg, "hasPublicMember "~ T.stringof ~ ", " ~ m);
1075     static if (__traits(hasMember, T, m)) {
1076         enum bool hasPublicMember = __traits(compiles, __traits(getMember, T, m));//(__traits(getProtection, __traits(getMember, T, m)) == "public");
1077     } else {
1078         enum bool hasPublicMember = false;
1079     }
1080 }
1081 
1082 //unittest {
1083 //    class Foo {
1084 //        int id;
1085 //        void x();
1086 //    }
1087 //    static assert(hasPublicMember!(Foo, "id"));
1088 //    static assert(hasPublicMember!(Foo, "x"));
1089 //    static assert(!hasPublicMember!(Foo, "zzz"));
1090 //
1091 //}
1092 
1093 /// returns true if it's object field of Embeddable object type
1094 template isEmbeddedObjectMember(T : Object, string m) {
1095     static if (isObjectMember!(T, m)) {
1096         alias typeof(__traits(getMember, T, m)) ti;
1097         enum bool isEmbeddedObjectMember = hasAnnotation!(getReferencedInstanceType!ti, Embeddable);
1098     } else {
1099         enum bool isEmbeddedObjectMember = false;
1100     }
1101 }
1102 
1103 template hasPublicField(T : Object, string m) {
1104     static if (hasPublicMember!(T, m)) {
1105         enum bool hasPublicField = !is(typeof(__traits(getMember, T, m)) == function) && !is(typeof(__traits(getMember, T, m)) == delegate);
1106     } else {
1107         enum bool hasPublicField = false;
1108     }
1109 }
1110 
1111 template hasPublicFieldWithAnnotation(T : Object, string m) {
1112     static if (hasPublicField!(T, m)) {
1113         enum bool hasPublicFieldWithAnnotation = hasDStructPropertyAnnotation!(T, m);
1114     } else {
1115         enum bool hasPublicFieldWithAnnotation = false;
1116     }
1117 }
1118 
1119 /// returns true if one of overloads of member m of class T is property setter with specified value type
1120 bool hasWritePropretyForType(T: Object, string m, ParamType)() {
1121     auto hasProperty = false;
1122     foreach(overload; MemberFunctionsTuple!(T, m)) {
1123         static if (ParameterTypeTuple!(overload).length == 1) {
1124             static if (functionAttributes!overload & FunctionAttribute.property) {
1125                 hasProperty = is(ParameterTypeTuple!(overload)[0] == ParamType);
1126                 break;
1127             }
1128         }
1129     }
1130     return hasProperty;
1131 }
1132 
1133 /// returns true if member m of class T has both property getter and setter of the same type
1134 bool isReadWriteProperty(T: Object, string m)() {
1135     bool res = false;
1136     foreach(overload; MemberFunctionsTuple!(T, m)) {
1137         static if (ParameterTypeTuple!(overload).length == 0) {
1138             static if (functionAttributes!overload & FunctionAttribute.property) {
1139                 res = hasWritePropretyForType!(T, m, ReturnType!overload);
1140                 break;
1141             }
1142         }
1143     }
1144     return res;
1145 }
1146 
1147 /// check that member m exists in class T, and it's function with single parameter of type ti
1148 template isValidSetter(T : Object, string m, ParamType) {
1149     // it's public member
1150     static if (hasPublicMember!(T, m)) {
1151         // it's function with single parameter of proper type
1152         enum bool isValidSetter = is(typeof(__traits(getMember, T, m)) == function) &&
1153                 ParameterTypeTuple!(typeof(__traits(getMember, T, m))).length == 1 &&
1154                 is(ParameterTypeTuple!(typeof(__traits(getMember, T, m)))[0] == ParamType);
1155     } else {
1156         enum bool isValidSetter = false; 
1157     }
1158 }
1159 
1160 template isValidGetter(T : Object, string m) {
1161     // it's public member with get or is prefix
1162     static if ((m.startsWith("get") || m.startsWith("is")) && hasPublicMember!(T, m)) {
1163         alias typeof(__traits(getMember, T, m)) ti;
1164         alias ReturnType!ti rti;
1165         // it's function
1166         static if (is(typeof(__traits(getMember, T, m)) == function)) {
1167             // function has no parameters
1168             static if (ParameterTypeTuple!(typeof(__traits(getMember, T, m))).length == 0) {
1169                 // has paired setter function of the same type
1170                 static if (isValidSetter!(T, getterNameToSetterName(m), rti)) {
1171                     enum bool isValidGetter = true; 
1172                 } else {
1173                     enum bool isValidGetter = false;
1174                 }
1175             }
1176         } else {
1177             enum bool isValidGetter = false; 
1178         }
1179     } else {
1180         enum bool isValidGetter = false; 
1181     }
1182 }
1183 
1184 template isValidGetterWithAnnotation(T : Object, string m) {
1185     // it's public member with get or is prefix
1186     static if (isValidGetter!(T, m)) {
1187         enum bool isValidGetterWithAnnotation = hasDStructPropertyAnnotation!(T,m); 
1188     } else {
1189         enum bool isValidGetterWithAnnotation = false; 
1190     }
1191 }
1192 
1193 bool isMainMemberForProperty(T : Object, string m)() {
1194     // skip non-public members
1195     static if (hasPublicMember!(T, m)) {
1196         alias typeof(__traits(getMember, T, m)) ti;
1197         immutable bool thisMemberHasAnnotation = hasDStructPropertyAnnotation!(T,m);
1198         static if (is(ti == function)) {
1199             // function or property
1200             static if (functionAttributes!ti & FunctionAttribute.property) {
1201                 // property
1202                 return isReadWriteProprety!(T, m);
1203             } else {
1204                 // getter function
1205                 // should have corresponding setter
1206                 static if (isValidGetter!(T,m)) {
1207                     // if any field with matching name is found, only one of them may have annotation
1208                     immutable bool annotatedField = hasPublicFieldWithAnnotation!(T, getterNameToFieldName(m)) || hasPublicFieldWithAnnotation!(T, "_" ~ getterNameToFieldName(m));
1209                     static assert(!annotatedField || !thisMemberHasAnnotation, "Both getter and corresponding field have annotations. Annotate only one of them.");
1210                     return !annotatedField;
1211                 } else {
1212                     // non-conventional name for getter or no setter
1213                     return false;
1214                 }
1215             }
1216         } else {
1217             // field
1218             //capitalizeFieldName
1219             immutable string gname = capitalizeFieldName(m);
1220             immutable bool hasAnnotadedGetter = isValidGetterWithAnnotation!(T, "get" ~ gname) || isValidGetterWithAnnotation!(T, "is" ~ gname);
1221             immutable bool hasGetter = isValidGetter!(T, "get" ~ gname) || isValidGetter!(T, "is" ~ gname);
1222             static assert (!thisMemberHasAnnotation || !hasAnnotadedGetter, "Both getter and corresponding field have annotations. Annotate only one of them.");
1223             return !hasAnnotadedGetter && (thisMemberHasAnnotation || !hasGetter);
1224         }
1225     } else {
1226         // member is not public
1227         return false;
1228     }
1229 }
1230 
1231 /// member is field or function or property returing SomeClass[] or LazyCollection!SomeClass
1232 template isCollectionMember(T : Object, string m) {
1233     alias typeof(__traits(getMember, T, m)) ti;
1234     static if (is(ti == function)) {
1235         static if (is(ReturnType!(typeof(__traits(getMember, T, m))) x == LazyCollection!Args, Args...))
1236             enum bool isCollectionMember = true;
1237         else {
1238             //pragma(msg, typeof(__traits(getMember, T, m).init[0]));
1239             alias ReturnType!ti rti;
1240             static if (isArray!rti && isImplicitlyConvertible!(typeof(rti.init[0]), Object))
1241                 enum bool isCollectionMember = true;
1242             else
1243                 enum bool isCollectionMember = false;
1244         }
1245     } else {
1246         static if (is(typeof(__traits(getMember, T, m)) x == LazyCollection!Args, Args...))
1247             enum bool isCollectionMember = true;
1248         else {
1249             //pragma(msg, typeof(__traits(getMember, T, m).init[0]));
1250             static if (isArray!(ti) && isImplicitlyConvertible!(typeof(__traits(getMember, T, m).init[0]), Object))
1251                 enum bool isCollectionMember = true;
1252             else
1253                 enum bool isCollectionMember = false;
1254         }
1255     }
1256 }
1257 
1258 unittest {
1259     class Foo {
1260         bool dummy;
1261     }
1262     struct Bar {
1263         bool dummy;
1264     }
1265     class MemberTest {
1266         bool simple;
1267         bool getSimple() { return simple; }
1268         int someInt;
1269         Long someLong;
1270         bool[] simples;
1271         bool[] getSimples() { return simples; }
1272         @property bool[] simpless() { return simples; }
1273         Foo foo;
1274         Foo getFoo() { return foo; }
1275         @property Foo fooo() { return foo; }
1276         Foo[] foos;
1277         Foo[] getFoos() { return foos; }
1278         @property Foo[] fooos() { return foos; }
1279         LazyCollection!Foo lfoos;
1280         ref LazyCollection!Foo lgetFoos() { return lfoos; }
1281         @property ref LazyCollection!Foo lfooos() { return lfoos; }
1282     }
1283     static assert(getColumnName!(MemberTest, "simple") == "simple");
1284     static assert(getColumnName!(MemberTest, "getSimple") == "simple");
1285     static assert(isObject!Foo);
1286     static assert(!isObject!Bar);
1287     static assert(!isObjectMember!(MemberTest, "simple"));
1288     static assert(!isObjectMember!(MemberTest, "simples"));
1289     static assert(!isObjectMember!(MemberTest, "getSimples"));
1290     static assert(!isObjectMember!(MemberTest, "simpless"));
1291     static assert(!isCollectionMember!(MemberTest, "simples"));
1292     static assert(!isCollectionMember!(MemberTest, "getSimples"));
1293     static assert(!isCollectionMember!(MemberTest, "simpless"));
1294     static assert(isObjectMember!(MemberTest, "foo"));
1295     static assert(isObjectMember!(MemberTest, "getFoo"));
1296     static assert(isObjectMember!(MemberTest, "fooo"));
1297     static assert(!isCollectionMember!(MemberTest, "simple"));
1298     static assert(!isCollectionMember!(MemberTest, "foo"));
1299     static assert(!isCollectionMember!(MemberTest, "getFoo"));
1300     static assert(!isCollectionMember!(MemberTest, "fooo"));
1301     static assert(isCollectionMember!(MemberTest, "foos"));
1302     static assert(isCollectionMember!(MemberTest, "getFoos"));
1303     static assert(isCollectionMember!(MemberTest, "fooos"));
1304     static assert(isCollectionMember!(MemberTest, "lfoos"));
1305     static assert(isCollectionMember!(MemberTest, "lgetFoos"));
1306     static assert(isCollectionMember!(MemberTest, "lfooos"));
1307     static assert(isSupportedSimpleType!(MemberTest, "simple"));
1308     static assert(!isSupportedSimpleType!(MemberTest, "foo"));
1309     static assert(isSupportedSimpleType!(MemberTest, "someInt"));
1310     static assert(isSupportedSimpleType!(MemberTest, "someLong"));
1311 }
1312 
1313 template getLazyInstanceType(T) {
1314     static if (is(T x == Lazy!Args, Args...))
1315         alias Args[0] getLazyInstanceType;
1316     else {
1317         static assert(false, "Not a Lazy! instance");
1318     }
1319 }
1320 
1321 template getLazyCollectionInstanceType(T) {
1322     static if (is(T x == LazyCollection!Args, Args...))
1323         alias Args[0] getLazyInstanceType;
1324     else {
1325         static assert(false, "Not a LazyCollection! instance");
1326     }
1327 }
1328 
1329 template getReferencedInstanceType(T) {
1330     //pragma(msg, T.stringof);
1331     static if (is(T == delegate)) {
1332         //pragma(msg, "is delegate");
1333         static if (isImplicitlyConvertible!(ReturnType!(T), Object)) {
1334             alias ReturnType!(T) getReferencedInstanceType;
1335         } else
1336             static assert(false, "@OneToOne, @ManyToOne, @OneToMany, @ManyToMany property can be only class or Lazy!class");
1337     } else static if (is(T == function)) {
1338         //pragma(msg, "is function");
1339         static if (isImplicitlyConvertible!(ReturnType!(T), Object)) {
1340             alias ReturnType!(T) getReferencedInstanceType;
1341         } else {
1342             static if (is(ReturnType!(T) x == Lazy!Args, Args...))
1343                 alias Args[0] getReferencedInstanceType;
1344             else
1345                 static assert(false, "Type cannot be used as relation " ~ T.stringof);
1346         }
1347     } else {
1348         //pragma(msg, "is not function");
1349         static if (is(T x == LazyCollection!Args, Args...)) {
1350             alias Args[0] getReferencedInstanceType;
1351         } else {
1352             static if (is(T x == Lazy!Args, Args...)) {
1353                 alias Args[0] getReferencedInstanceType;
1354             } else {
1355                 static if (isArray!(T)) {
1356                     static if (isImplicitlyConvertible!(typeof(T.init[0]), Object)) {
1357                         //pragma(msg, "isImplicitlyConvertible!(T, Object)");
1358                         alias typeof(T.init[0]) getReferencedInstanceType;
1359                     } else {
1360                         static assert(false, "Type cannot be used as relation " ~ T.stringof);
1361                     }
1362                 } else static if (isImplicitlyConvertible!(T, Object)) {
1363                     //pragma(msg, "isImplicitlyConvertible!(T, Object)");
1364                     alias T getReferencedInstanceType;
1365                 } else static if (isImplicitlyConvertible!(T, Object[])) {
1366                     //pragma(msg, "isImplicitlyConvertible!(T, Object)");
1367                     alias T getReferencedInstanceType;
1368                 } else {
1369                     static assert(false, "Type cannot be used as relation " ~ T.stringof);
1370                 }
1371             }
1372         }
1373     }
1374 }
1375 
1376 string getPropertyReferencedEntityName(T : Object, string m)() {
1377     alias typeof(__traits(getMember, T, m)) ti;
1378     return getEntityName!(getReferencedInstanceType!ti);
1379 }
1380 
1381 string getPropertyEmbeddedClassName(T : Object, string m)() {
1382     alias typeof(__traits(getMember, T, m)) ti;
1383     static if (is(ti == function)) {
1384         static if (isImplicitlyConvertible!(ReturnType!(ti), Object)) {
1385             static assert(hasAnnotation!(ReturnType!(ti), Embeddable), "@Embedded property class should have @Embeddable annotation");
1386             return fullyQualifiedName!(ReturnType!(ti));
1387         } else
1388             static assert(false, "@Embedded property can be only class with @Embeddable annotation");
1389     } else {
1390         static if (isImplicitlyConvertible!(ti, Object)) {
1391             static assert(hasAnnotation!(ti, Embeddable), "@Embedded property class should have @Embeddable annotation");
1392             return fullyQualifiedName!ti;
1393         } else 
1394             static assert(false, "@Embedded property can be only class with @Embeddable annotation");
1395     }
1396 }
1397 
1398 string getPropertyReferencedClassName(T : Object, string m)() {
1399     alias typeof(__traits(getMember, T, m)) ti;
1400     return fullyQualifiedName!(getReferencedInstanceType!ti);
1401 }
1402 
1403 
1404 
1405 
1406 
1407 enum PropertyMemberType : int {
1408     BOOL_TYPE,    // bool
1409     BYTE_TYPE,    // byte
1410     SHORT_TYPE,   // short
1411     INT_TYPE,     // int
1412     LONG_TYPE,    // long
1413     UBYTE_TYPE,   // ubyte
1414     USHORT_TYPE,  // ushort
1415     UINT_TYPE,    // uint
1416     ULONG_TYPE,   // ulong
1417     NULLABLE_BYTE_TYPE,  // Nullable!byte
1418     NULLABLE_SHORT_TYPE, // Nullable!short
1419     NULLABLE_INT_TYPE,   // Nullable!int
1420     NULLABLE_LONG_TYPE,  // Nullable!long
1421     NULLABLE_UBYTE_TYPE, // Nullable!ubyte
1422     NULLABLE_USHORT_TYPE,// Nullable!ushort
1423     NULLABLE_UINT_TYPE,  // Nullable!uint
1424     NULLABLE_ULONG_TYPE, // Nullable!ulong
1425     FLOAT_TYPE,   // float
1426     DOUBLE_TYPE,   // double
1427     NULLABLE_FLOAT_TYPE, // Nullable!float
1428     NULLABLE_DOUBLE_TYPE,// Nullable!double
1429     STRING_TYPE,   // string
1430     NULLABLE_STRING_TYPE,   // nullable string - String struct
1431     DATETIME_TYPE, // std.datetime.DateTime
1432     DATE_TYPE, // std.datetime.Date
1433     TIME_TYPE, // std.datetime.TimeOfDay
1434     NULLABLE_DATETIME_TYPE, // Nullable!std.datetime.DateTime
1435     NULLABLE_DATE_TYPE, // Nullable!std.datetime.Date
1436     NULLABLE_TIME_TYPE, // Nullable!std.datetime.TimeOfDay
1437     BYTE_ARRAY_TYPE, // byte[]
1438     UBYTE_ARRAY_TYPE, // ubyte[]
1439 }
1440 
1441 template isSupportedSimpleType(T, string m) {
1442     alias typeof(__traits(getMember, T, m)) ti;
1443     static if (is(ti == function)) {
1444         static if (is(ReturnType!(ti) == bool)) {
1445             enum bool isSupportedSimpleType = true;
1446         } else static if (is(ReturnType!(ti) == byte)) {
1447             enum bool isSupportedSimpleType = true;
1448         } else static if (is(ReturnType!(ti) == short)) {
1449             enum bool isSupportedSimpleType = true;
1450         } else static if (is(ReturnType!(ti) == int)) {
1451             enum bool isSupportedSimpleType = true;
1452         } else static if (is(ReturnType!(ti) == long)) {
1453             enum bool isSupportedSimpleType = true;
1454         } else static if (is(ReturnType!(ti) == ubyte)) {
1455             enum bool isSupportedSimpleType = true;
1456         } else static if (is(ReturnType!(ti) == ushort)) {
1457             enum bool isSupportedSimpleType = true;
1458         } else static if (is(ReturnType!(ti) == uint)) {
1459             enum bool isSupportedSimpleType = true;
1460         } else static if (is(ReturnType!(ti) == ulong)) {
1461             enum bool isSupportedSimpleType = true;
1462         } else static if (is(ReturnType!(ti) == float)) {
1463             enum bool isSupportedSimpleType = true;
1464         } else static if (is(ReturnType!(ti) == double)) {
1465             enum bool isSupportedSimpleType = true;
1466         } else static if (is(ReturnType!(ti) == Nullable!byte)) {
1467             enum bool isSupportedSimpleType = true;
1468         } else static if (is(ReturnType!(ti) == Nullable!short)) {
1469             enum bool isSupportedSimpleType = true;
1470         } else static if (is(ReturnType!(ti) == Nullable!int)) {
1471             enum bool isSupportedSimpleType = true;
1472         } else static if (is(ReturnType!(ti) == Nullable!long)) {
1473             enum bool isSupportedSimpleType = true;
1474         } else static if (is(ReturnType!(ti) == Nullable!ubyte)) {
1475             enum bool isSupportedSimpleType = true;
1476         } else static if (is(ReturnType!(ti) == Nullable!ushort)) {
1477             enum bool isSupportedSimpleType = true;
1478         } else static if (is(ReturnType!(ti) == Nullable!uint)) {
1479             enum bool isSupportedSimpleType = true;
1480         } else static if (is(ReturnType!(ti) == Nullable!ulong)) {
1481             enum bool isSupportedSimpleType = true;
1482         } else static if (is(ReturnType!(ti) == Nullable!float)) {
1483             enum bool isSupportedSimpleType = true;
1484         } else static if (is(ReturnType!(ti) == Nullable!double)) {
1485             enum bool isSupportedSimpleType = true;
1486         } else static if (is(ReturnType!(ti) == string)) {
1487             enum bool isSupportedSimpleType = true;
1488         } else static if (is(ReturnType!(ti) == dstruct.type.String)) {
1489             enum bool isSupportedSimpleType = true;
1490         } else static if (is(ReturnType!(ti) == DateTime)) {
1491             enum bool isSupportedSimpleType = true;
1492         } else static if (is(ReturnType!(ti) == Date)) {
1493             enum bool isSupportedSimpleType = true;
1494         } else static if (is(ReturnType!(ti) == TimeOfDay)) {
1495             enum bool isSupportedSimpleType = true;
1496         } else static if (is(ReturnType!(ti) == Nullable!DateTime)) {
1497             enum bool isSupportedSimpleType = true;
1498         } else static if (is(ReturnType!(ti) == Nullable!Date)) {
1499             enum bool isSupportedSimpleType = true;
1500         } else static if (is(ReturnType!(ti) == Nullable!TimeOfDay)) {
1501             enum bool isSupportedSimpleType = true;
1502         } else static if (is(ReturnType!(ti) == byte[])) {
1503             enum bool isSupportedSimpleType = true;
1504         } else static if (is(ReturnType!(ti) == ubyte[])) {
1505             enum bool isSupportedSimpleType = true;
1506         } else {
1507             enum bool isSupportedSimpleType = false;
1508         }
1509     } else static if (is(ti == bool)) {
1510         enum bool isSupportedSimpleType = true;
1511     } else static if (is(ti == byte)) {
1512         enum bool isSupportedSimpleType = true;
1513     } else static if (is(ti == short)) {
1514         enum bool isSupportedSimpleType = true;
1515     } else static if (is(ti == int)) {
1516         enum bool isSupportedSimpleType = true;
1517     } else static if (is(ti == long)) {
1518         enum bool isSupportedSimpleType = true;
1519     } else static if (is(ti == ubyte)) {
1520         enum bool isSupportedSimpleType = true;
1521     } else static if (is(ti == ushort)) {
1522         enum bool isSupportedSimpleType = true;
1523     } else static if (is(ti == uint)) {
1524         enum bool isSupportedSimpleType = true;
1525     } else static if (is(ti == ulong)) {
1526         enum bool isSupportedSimpleType = true;
1527     } else static if (is(ti == float)) {
1528         enum bool isSupportedSimpleType = true;
1529     } else static if (is(ti == double)) {
1530         enum bool isSupportedSimpleType = true;
1531     } else static if (is(ti == Nullable!byte)) {
1532         enum bool isSupportedSimpleType = true;
1533     } else static if (is(ti == Nullable!short)) {
1534         enum bool isSupportedSimpleType = true;
1535     } else static if (is(ti == Nullable!int)) {
1536         enum bool isSupportedSimpleType = true;
1537     } else static if (is(ti == Nullable!long)) {
1538         enum bool isSupportedSimpleType = true;
1539     } else static if (is(ti == Nullable!ubyte)) {
1540         enum bool isSupportedSimpleType = true;
1541     } else static if (is(ti == Nullable!ushort)) {
1542         enum bool isSupportedSimpleType = true;
1543     } else static if (is(ti == Nullable!uint)) {
1544         enum bool isSupportedSimpleType = true;
1545     } else static if (is(ti == Nullable!ulong)) {
1546         enum bool isSupportedSimpleType = true;
1547     } else static if (is(ti == Nullable!float)) {
1548         enum bool isSupportedSimpleType = true;
1549     } else static if (is(ti == Nullable!double)) {
1550         enum bool isSupportedSimpleType = true;
1551     } else static if (is(ti == string)) {
1552         enum bool isSupportedSimpleType = true;
1553     } else static if (is(ti == dstruct.type.String)) {
1554         enum bool isSupportedSimpleType = true;
1555     } else static if (is(ti == DateTime)) {
1556         enum bool isSupportedSimpleType = true;
1557     } else static if (is(ti == Date)) {
1558         enum bool isSupportedSimpleType = true;
1559     } else static if (is(ti == TimeOfDay)) {
1560         enum bool isSupportedSimpleType = true;
1561     } else static if (is(ti == Nullable!DateTime)) {
1562         enum bool isSupportedSimpleType = true;
1563     } else static if (is(ti == Nullable!Date)) {
1564         enum bool isSupportedSimpleType = true;
1565     } else static if (is(ti == Nullable!TimeOfDay)) {
1566         enum bool isSupportedSimpleType = true;
1567     } else static if (is(ti == byte[])) {
1568         enum bool isSupportedSimpleType = true;
1569     } else static if (is(ti == ubyte[])) {
1570         enum bool isSupportedSimpleType = true;
1571     } else {
1572         enum bool isSupportedSimpleType = false;
1573     }
1574 }
1575 
1576 PropertyMemberType getPropertyMemberType(T, string m)() {
1577     alias typeof(__traits(getMember, T, m)) ti;
1578     static if (is(ti == function)) {
1579 		static if (is(ReturnType!(ti) == bool)) {
1580 			return PropertyMemberType.BOOL_TYPE;
1581 		} else static if (is(ReturnType!(ti) == byte)) {
1582             return PropertyMemberType.BYTE_TYPE;
1583         } else if (is(ReturnType!(ti) == short)) {
1584             return PropertyMemberType.SHORT_TYPE;
1585         } else if (is(ReturnType!(ti) == int)) {
1586             return PropertyMemberType.INT_TYPE;
1587         } else if (is(ReturnType!(ti) == long)) {
1588             return PropertyMemberType.LONG_TYPE;
1589         } else if (is(ReturnType!(ti) == ubyte)) {
1590             return PropertyMemberType.UBYTE_TYPE;
1591         } else if (is(ReturnType!(ti) == ushort)) {
1592             return PropertyMemberType.USHORT_TYPE;
1593         } else if (is(ReturnType!(ti) == uint)) {
1594             return PropertyMemberType.UINT_TYPE;
1595         } else if (is(ReturnType!(ti) == ulong)) {
1596             return PropertyMemberType.ULONG_TYPE;
1597         } else if (is(ReturnType!(ti) == float)) {
1598             return PropertyMemberType.FLOAT_TYPE;
1599         } else if (is(ReturnType!(ti) == double)) {
1600             return PropertyMemberType.DOUBLE_TYPE;
1601         } else if (is(ReturnType!(ti) == Nullable!byte)) {
1602             return PropertyMemberType.NULLABLE_BYTE_TYPE;
1603         } else if (is(ReturnType!(ti) == Nullable!short)) {
1604             return PropertyMemberType.NULLABLE_SHORT_TYPE;
1605         } else if (is(ReturnType!(ti) == Nullable!int)) {
1606             return PropertyMemberType.NULLABLE_INT_TYPE;
1607         } else if (is(ReturnType!(ti) == Nullable!long)) {
1608             return PropertyMemberType.NULLABLE_LONG_TYPE;
1609         } else if (is(ReturnType!(ti) == Nullable!ubyte)) {
1610             return PropertyMemberType.NULLABLE_UBYTE_TYPE;
1611         } else if (is(ReturnType!(ti) == Nullable!ushort)) {
1612             return PropertyMemberType.NULLABLE_USHORT_TYPE;
1613         } else if (is(ReturnType!(ti) == Nullable!uint)) {
1614             return PropertyMemberType.NULLABLE_UINT_TYPE;
1615         } else if (is(ReturnType!(ti) == Nullable!ulong)) {
1616             return PropertyMemberType.NULLABLE_ULONG_TYPE;
1617         } else if (is(ReturnType!(ti) == Nullable!float)) {
1618             return PropertyMemberType.NULLABLE_FLOAT_TYPE;
1619         } else if (is(ReturnType!(ti) == Nullable!double)) {
1620             return PropertyMemberType.NULLABLE_DOUBLE_TYPE;
1621         } else if (is(ReturnType!(ti) == string)) {
1622             return PropertyMemberType.STRING_TYPE;
1623         } else if (is(ReturnType!(ti) == String)) {
1624             return PropertyMemberType.NULLABLE_STRING_TYPE;
1625         } else if (is(ReturnType!(ti) == DateTime)) {
1626             return PropertyMemberType.DATETIME_TYPE;
1627         } else if (is(ReturnType!(ti) == Date)) {
1628             return PropertyMemberType.DATE_TYPE;
1629         } else if (is(ReturnType!(ti) == TimeOfDay)) {
1630             return PropertyMemberType.TIME_TYPE;
1631         } else if (is(ReturnType!(ti) == Nullable!DateTime)) {
1632             return PropertyMemberType.NULLABLE_DATETIME_TYPE;
1633         } else if (is(ReturnType!(ti) == Nullable!Date)) {
1634             return PropertyMemberType.NULLABLE_DATE_TYPE;
1635         } else if (is(ReturnType!(ti) == Nullable!TimeOfDay)) {
1636             return PropertyMemberType.NULLABLE_TIME_TYPE;
1637         } else if (is(ReturnType!(ti) == byte[])) {
1638             return PropertyMemberType.BYTE_ARRAY_TYPE;
1639         } else if (is(ReturnType!(ti) == ubyte[])) {
1640             return PropertyMemberType.UBYTE_ARRAY_TYPE;
1641         } else {
1642             assert (false, "Member " ~ m ~ " of class " ~ T.stringof ~ " has unsupported type " ~ ti.stringof);
1643         }
1644 	} else if (is(ti == bool)) {
1645 		return PropertyMemberType.BOOL_TYPE;
1646     } else if (is(ti == byte)) {
1647         return PropertyMemberType.BYTE_TYPE;
1648     } else if (is(ti == short)) {
1649         return PropertyMemberType.SHORT_TYPE;
1650     } else if (is(ti == int)) {
1651         return PropertyMemberType.INT_TYPE;
1652     } else if (is(ti == long)) {
1653         return PropertyMemberType.LONG_TYPE;
1654     } else if (is(ti == ubyte)) {
1655         return PropertyMemberType.UBYTE_TYPE;
1656     } else if (is(ti == ushort)) {
1657         return PropertyMemberType.USHORT_TYPE;
1658     } else if (is(ti == uint)) {
1659         return PropertyMemberType.UINT_TYPE;
1660     } else if (is(ti == ulong)) {
1661         return PropertyMemberType.ULONG_TYPE;
1662     } else if (is(ti == float)) {
1663         return PropertyMemberType.FLOAT_TYPE;
1664     } else if (is(ti == double)) {
1665         return PropertyMemberType.DOUBLE_TYPE;
1666     } else if (is(ti == Nullable!byte)) {
1667         return PropertyMemberType.NULLABLE_BYTE_TYPE;
1668     } else if (is(ti == Nullable!short)) {
1669         return PropertyMemberType.NULLABLE_SHORT_TYPE;
1670     } else if (is(ti == Nullable!int)) {
1671         return PropertyMemberType.NULLABLE_INT_TYPE;
1672     } else if (is(ti == Nullable!long)) {
1673         return PropertyMemberType.NULLABLE_LONG_TYPE;
1674     } else if (is(ti == Nullable!ubyte)) {
1675         return PropertyMemberType.NULLABLE_UBYTE_TYPE;
1676     } else if (is(ti == Nullable!ushort)) {
1677         return PropertyMemberType.NULLABLE_USHORT_TYPE;
1678     } else if (is(ti == Nullable!uint)) {
1679         return PropertyMemberType.NULLABLE_UINT_TYPE;
1680     } else if (is(ti == Nullable!ulong)) {
1681         return PropertyMemberType.NULLABLE_ULONG_TYPE;
1682     } else if (is(ti == Nullable!float)) {
1683         return PropertyMemberType.NULLABLE_FLOAT_TYPE;
1684     } else if (is(ti == Nullable!double)) {
1685         return PropertyMemberType.NULLABLE_DOUBLE_TYPE;
1686     } else if (is(ti == string)) {
1687         return PropertyMemberType.STRING_TYPE;
1688     } else if (is(ti == dstruct.type.String)) {
1689         return PropertyMemberType.NULLABLE_STRING_TYPE;
1690     } else if (is(ti == DateTime)) {
1691         return PropertyMemberType.DATETIME_TYPE;
1692     } else if (is(ti == Date)) {
1693         return PropertyMemberType.DATE_TYPE;
1694     } else if (is(ti == TimeOfDay)) {
1695         return PropertyMemberType.TIME_TYPE;
1696     } else if (is(ti == Nullable!DateTime)) {
1697         return PropertyMemberType.NULLABLE_DATETIME_TYPE;
1698     } else if (is(ti == Nullable!Date)) {
1699         return PropertyMemberType.NULLABLE_DATE_TYPE;
1700     } else if (is(ti == Nullable!TimeOfDay)) {
1701         return PropertyMemberType.NULLABLE_TIME_TYPE;
1702     } else if (is(ti == byte[])) {
1703         return PropertyMemberType.BYTE_ARRAY_TYPE;
1704     } else if (is(ti == ubyte[])) {
1705         return PropertyMemberType.UBYTE_ARRAY_TYPE;
1706     } else {
1707         assert (false, "Member " ~ m ~ " of class " ~ T.stringof ~ " has unsupported type " ~ ti.stringof);
1708     }
1709     //static assert (false, "Member " ~ m ~ " of class " ~ T.stringof ~ " has unsupported type " ~ ti.stringof);
1710 }
1711 
1712 
1713 string getPropertyReadCode(T, string m)() {
1714     return substituteParam(PropertyMemberKind_ReadCode[getPropertyMemberKind!(T,m)()], m);
1715 }
1716 
1717 static immutable bool[] ColumnTypeCanHoldNulls = 
1718     [
1719      false, //BOOL_TYPE     // bool
1720      false, //BYTE_TYPE,    // byte
1721      false, //SHORT_TYPE,   // short
1722      false, //INT_TYPE,     // int
1723      false, //LONG_TYPE,    // long
1724      false, //UBYTE_TYPE,   // ubyte
1725      false, //USHORT_TYPE,  // ushort
1726      false, //UINT_TYPE,    // uint
1727      false, //ULONG_TYPE,   // ulong
1728      true, //NULLABLE_BYTE_TYPE,  // Nullable!byte
1729      true, //NULLABLE_SHORT_TYPE, // Nullable!short
1730      true, //NULLABLE_INT_TYPE,   // Nullable!int
1731      true, //NULLABLE_LONG_TYPE,  // Nullable!long
1732      true, //NULLABLE_UBYTE_TYPE, // Nullable!ubyte
1733      true, //NULLABLE_USHORT_TYPE,// Nullable!ushort
1734      true, //NULLABLE_UINT_TYPE,  // Nullable!uint
1735      true, //NULLABLE_ULONG_TYPE, // Nullable!ulong
1736      false,//FLOAT_TYPE,   // float
1737      false,//DOUBLE_TYPE,   // double
1738      true, //NULLABLE_FLOAT_TYPE, // Nullable!float
1739      true, //NULLABLE_DOUBLE_TYPE,// Nullable!double
1740      false, //STRING_TYPE   // string  -- treat as @NotNull by default
1741      true, //NULLABLE_STRING_TYPE   // String
1742      false, //DATETIME_TYPE, // std.datetime.DateTime
1743      false, //DATE_TYPE, // std.datetime.Date
1744      false, //TIME_TYPE, // std.datetime.TimeOfDay
1745      true, //NULLABLE_DATETIME_TYPE, // Nullable!std.datetime.DateTime
1746      true, //NULLABLE_DATE_TYPE, // Nullable!std.datetime.Date
1747      true, //NULLABLE_TIME_TYPE, // Nullable!std.datetime.TimeOfDay
1748      true, //BYTE_ARRAY_TYPE, // byte[]
1749      true, //UBYTE_ARRAY_TYPE, // ubyte[]
1750      ];
1751 
1752 bool isColumnTypeNullableByDefault(T, string m)() {
1753     return ColumnTypeCanHoldNulls[getPropertyMemberType!(T,m)];
1754 }
1755 
1756 
1757 static immutable string[] ColumnTypeKeyIsSetCode = 
1758     [
1759      "(%s != 0)", //BOOL_TYPE     // bool
1760      "(%s != 0)", //BYTE_TYPE,    // byte
1761      "(%s != 0)", //SHORT_TYPE,   // short
1762      "(%s != 0)", //INT_TYPE,     // int
1763      "(%s != 0)", //LONG_TYPE,    // long
1764      "(%s != 0)", //UBYTE_TYPE,   // ubyte
1765      "(%s != 0)", //USHORT_TYPE,  // ushort
1766      "(%s != 0)", //UINT_TYPE,    // uint
1767      "(%s != 0)", //ULONG_TYPE,   // ulong
1768      "(!%s.isNull)", //NULLABLE_BYTE_TYPE,  // Nullable!byte
1769      "(!%s.isNull)", //NULLABLE_SHORT_TYPE, // Nullable!short
1770      "(!%s.isNull)", //NULLABLE_INT_TYPE,   // Nullable!int
1771      "(!%s.isNull)", //NULLABLE_LONG_TYPE,  // Nullable!long
1772      "(!%s.isNull)", //NULLABLE_UBYTE_TYPE, // Nullable!ubyte
1773      "(!%s.isNull)", //NULLABLE_USHORT_TYPE,// Nullable!ushort
1774      "(!%s.isNull)", //NULLABLE_UINT_TYPE,  // Nullable!uint
1775      "(!%s.isNull)", //NULLABLE_ULONG_TYPE, // Nullable!ulong
1776      "(%s != 0)",//FLOAT_TYPE,   // float
1777      "(%s != 0)",//DOUBLE_TYPE,   // double
1778      "(!%s.isNull)", //NULLABLE_FLOAT_TYPE, // Nullable!float
1779      "(!%s.isNull)", //NULLABLE_DOUBLE_TYPE,// Nullable!double
1780      "(%s !is null)", //STRING_TYPE   // string
1781      "(%s !is null)", //NULLABLE_STRING_TYPE   // String
1782      "(%s != DateTime())", //DATETIME_TYPE, // std.datetime.DateTime
1783      "(%s != Date())", //DATE_TYPE, // std.datetime.Date
1784      "(%s != TimeOfDay())", //TIME_TYPE, // std.datetime.TimeOfDay
1785      "(!%s.isNull)", //NULLABLE_DATETIME_TYPE, // Nullable!std.datetime.DateTime
1786      "(!%s.isNull)", //NULLABLE_DATE_TYPE, // Nullable!std.datetime.Date
1787      "(!%s.isNull)", //NULLABLE_TIME_TYPE, // Nullable!std.datetime.TimeOfDay
1788      "(%s !is null)", //BYTE_ARRAY_TYPE, // byte[]
1789      "(%s !is null)", //UBYTE_ARRAY_TYPE, // ubyte[]
1790      ];
1791 
1792 string getColumnTypeKeyIsSetCode(T, string m)() {
1793     return substituteParam(ColumnTypeKeyIsSetCode[getPropertyMemberType!(T,m)()], getPropertyReadCode!(T,m)());
1794 }
1795 
1796 static immutable string[] ColumnTypeIsNullCode = 
1797     [
1798      "(false)", //BOOL_TYPE     // bool
1799      "(false)", //BYTE_TYPE,    // byte
1800      "(false)", //SHORT_TYPE,   // short
1801      "(false)", //INT_TYPE,     // int
1802      "(false)", //LONG_TYPE,    // long
1803      "(false)", //UBYTE_TYPE,   // ubyte
1804      "(false)", //USHORT_TYPE,  // ushort
1805      "(false)", //UINT_TYPE,    // uint
1806      "(false)", //ULONG_TYPE,   // ulong
1807      "(%s.isNull)", //NULLABLE_BYTE_TYPE,  // Nullable!byte
1808      "(%s.isNull)", //NULLABLE_SHORT_TYPE, // Nullable!short
1809      "(%s.isNull)", //NULLABLE_INT_TYPE,   // Nullable!int
1810      "(%s.isNull)", //NULLABLE_LONG_TYPE,  // Nullable!long
1811      "(%s.isNull)", //NULLABLE_UBYTE_TYPE, // Nullable!ubyte
1812      "(%s.isNull)", //NULLABLE_USHORT_TYPE,// Nullable!ushort
1813      "(%s.isNull)", //NULLABLE_UINT_TYPE,  // Nullable!uint
1814      "(%s.isNull)", //NULLABLE_ULONG_TYPE, // Nullable!ulong
1815      "(false)",//FLOAT_TYPE,   // float
1816      "(false)",//DOUBLE_TYPE,   // double
1817      "(%s.isNull)", //NULLABLE_FLOAT_TYPE, // Nullable!float
1818      "(%s.isNull)", //NULLABLE_DOUBLE_TYPE,// Nullable!double
1819      "(%s is null)", //STRING_TYPE   // string
1820      "(%s is null)", //NULLABLE_STRING_TYPE   // String
1821      "(false)", //DATETIME_TYPE, // std.datetime.DateTime
1822      "(false)", //DATE_TYPE, // std.datetime.Date
1823      "(false)", //TIME_TYPE, // std.datetime.TimeOfDay
1824      "(%s.isNull)", //NULLABLE_DATETIME_TYPE, // Nullable!std.datetime.DateTime
1825      "(%s.isNull)", //NULLABLE_DATE_TYPE, // Nullable!std.datetime.Date
1826      "(%s.isNull)", //NULLABLE_TIME_TYPE, // Nullable!std.datetime.TimeOfDay
1827      "(%s is null)", //BYTE_ARRAY_TYPE, // byte[]
1828      "(%s is null)", //UBYTE_ARRAY_TYPE, // ubyte[]
1829      ];
1830 
1831 string getColumnTypeIsNullCode(T, string m)() {
1832     return substituteParam(ColumnTypeIsNullCode[getPropertyMemberType!(T,m)()], getPropertyReadCode!(T,m)());
1833 }
1834 
1835 static immutable string[] ColumnTypeSetNullCode = 
1836     [
1837      "bool nv;", // BOOL_TYPE   // bool
1838      "byte nv = 0;", //BYTE_TYPE,    // byte
1839      "short nv = 0;", //SHORT_TYPE,   // short
1840      "int nv = 0;", //INT_TYPE,     // int
1841      "long nv = 0;", //LONG_TYPE,    // long
1842      "ubyte nv = 0;", //UBYTE_TYPE,   // ubyte
1843      "ushort nv = 0;", //USHORT_TYPE,  // ushort
1844      "uint nv = 0;", //UINT_TYPE,    // uint
1845      "ulong nv = 0;", //ULONG_TYPE,   // ulong
1846      "Nullable!byte nv;", //NULLABLE_BYTE_TYPE,  // Nullable!byte
1847      "Nullable!short nv;", //NULLABLE_SHORT_TYPE, // Nullable!short
1848      "Nullable!int nv;", //NULLABLE_INT_TYPE,   // Nullable!int
1849      "Nullable!long nv;", //NULLABLE_LONG_TYPE,  // Nullable!long
1850      "Nullable!ubyte nv;", //NULLABLE_UBYTE_TYPE, // Nullable!ubyte
1851      "Nullable!ushort nv;", //NULLABLE_USHORT_TYPE,// Nullable!ushort
1852      "Nullable!uint nv;", //NULLABLE_UINT_TYPE,  // Nullable!uint
1853      "Nullable!ulong nv;", //NULLABLE_ULONG_TYPE, // Nullable!ulong
1854      "float nv = 0;",//FLOAT_TYPE,   // float
1855      "double nv = 0;",//DOUBLE_TYPE,   // double
1856      "Nullable!float nv;", //NULLABLE_FLOAT_TYPE, // Nullable!float
1857      "Nullable!double nv;", //NULLABLE_DOUBLE_TYPE,// Nullable!double
1858      "string nv;", //STRING_TYPE   // string
1859      "string nv;", //NULLABLE_STRING_TYPE   // String
1860      "DateTime nv;", //DATETIME_TYPE, // std.datetime.DateTime
1861      "Date nv;", //DATE_TYPE, // std.datetime.Date
1862      "TimeOfDay nv;", //TIME_TYPE, // std.datetime.TimeOfDay
1863      "Nullable!DateTime nv;", //NULLABLE_DATETIME_TYPE, // Nullable!std.datetime.DateTime
1864      "Nullable!Date nv;", //NULLABLE_DATE_TYPE, // Nullable!std.datetime.Date
1865      "Nullable!TimeOfDay nv;", //NULLABLE_TIME_TYPE, // Nullable!std.datetime.TimeOfDay
1866      "byte[] nv = null;", //BYTE_ARRAY_TYPE, // byte[]
1867      "ubyte[] nv = null;", //UBYTE_ARRAY_TYPE, // ubyte[]
1868      ];
1869 
1870 static immutable string[] ColumnTypePropertyToVariant = 
1871     [
1872      "Variant(%s)", //BOOL_TYPE     // bool
1873      "Variant(%s)", //BYTE_TYPE,    // byte
1874      "Variant(%s)", //SHORT_TYPE,   // short
1875      "Variant(%s)", //INT_TYPE,     // int
1876      "Variant(%s)", //LONG_TYPE,    // long
1877      "Variant(%s)", //UBYTE_TYPE,   // ubyte
1878      "Variant(%s)", //USHORT_TYPE,  // ushort
1879      "Variant(%s)", //UINT_TYPE,    // uint
1880      "Variant(%s)", //ULONG_TYPE,   // ulong
1881      "(%s.isNull ? Variant(null) : Variant(%s.get()))", //NULLABLE_BYTE_TYPE,  // Nullable!byte
1882      "(%s.isNull ? Variant(null) : Variant(%s.get()))", //NULLABLE_SHORT_TYPE, // Nullable!short
1883      "(%s.isNull ? Variant(null) : Variant(%s.get()))", //NULLABLE_INT_TYPE,   // Nullable!int
1884      "(%s.isNull ? Variant(null) : Variant(%s.get()))", //NULLABLE_LONG_TYPE,  // Nullable!long
1885      "(%s.isNull ? Variant(null) : Variant(%s.get()))", //NULLABLE_UBYTE_TYPE, // Nullable!ubyte
1886      "(%s.isNull ? Variant(null) : Variant(%s.get()))", //NULLABLE_USHORT_TYPE,// Nullable!ushort
1887      "(%s.isNull ? Variant(null) : Variant(%s.get()))", //NULLABLE_UINT_TYPE,  // Nullable!uint
1888      "(%s.isNull ? Variant(null) : Variant(%s.get()))", //NULLABLE_ULONG_TYPE, // Nullable!ulong
1889      "Variant(%s)",//FLOAT_TYPE,   // float
1890      "Variant(%s)",//DOUBLE_TYPE,   // double
1891      "(%s.isNull ? Variant(null) : Variant(%s.get()))", //NULLABLE_FLOAT_TYPE, // Nullable!float
1892      "(%s.isNull ? Variant(null) : Variant(%s.get()))", //NULLABLE_DOUBLE_TYPE,// Nullable!double
1893      "Variant(%s)", //STRING_TYPE   // string
1894      "Variant(%s)", //NULLABLE_STRING_TYPE   // String
1895      "Variant(%s)", //DATETIME_TYPE, // std.datetime.DateTime
1896      "Variant(%s)", //DATE_TYPE, // std.datetime.Date
1897      "Variant(%s)", //TIME_TYPE, // std.datetime.TimeOfDay
1898      "(%s.isNull ? Variant(null) : Variant(%s.get()))", //NULLABLE_DATETIME_TYPE, // Nullable!std.datetime.DateTime
1899      "(%s.isNull ? Variant(null) : Variant(%s.get()))", //NULLABLE_DATE_TYPE, // Nullable!std.datetime.Date
1900      "(%s.isNull ? Variant(null) : Variant(%s.get()))", //NULLABLE_TIME_TYPE, // Nullable!std.datetime.TimeOfDay
1901      "Variant(%s)", //BYTE_ARRAY_TYPE, // byte[]
1902      "Variant(%s)", //UBYTE_ARRAY_TYPE, // ubyte[]
1903      ];
1904 
1905 string getPropertyWriteCode(T, string m)() {
1906     immutable PropertyMemberKind kind = getPropertyMemberKind!(T, m)();
1907     immutable string nullValueCode = ColumnTypeSetNullCode[getPropertyMemberType!(T,m)()];
1908     immutable string datasetReader = "(!r.isNull(index) ? " ~ getColumnTypeDatasetReadCode!(T, m)() ~ " : nv)";
1909     final switch (kind) {
1910         case PropertyMemberKind.FIELD_MEMBER:
1911             return nullValueCode ~ "entity." ~ m ~ " = " ~ datasetReader ~ ";";
1912         case PropertyMemberKind.LAZY_MEMBER:
1913             return nullValueCode ~ "entity." ~ m ~ " = " ~ datasetReader ~ ";";
1914         case PropertyMemberKind.GETTER_MEMBER:
1915             return nullValueCode ~ "entity." ~ getterNameToSetterName(m) ~ "(" ~ datasetReader ~ ");";
1916         case PropertyMemberKind.PROPERTY_MEMBER:
1917             return nullValueCode ~ "entity." ~ m ~ " = " ~ datasetReader ~ ";";
1918         case PropertyMemberKind.UNSUPPORTED_MEMBER:
1919             assert(false, "Unsupported member kind " ~ T.stringof ~ "." ~ m ~ " " ~ typeof(__traits(getMember, T, m)).stringof);
1920     }
1921 }
1922 
1923 string getPropertyCopyCode(T, string m)() {
1924     immutable PropertyMemberKind kind = getPropertyMemberKind!(T, m)();
1925     final switch (kind) {
1926         case PropertyMemberKind.FIELD_MEMBER:
1927             return "toentity." ~ m ~ " = fromentity." ~ m ~ ";";
1928         case PropertyMemberKind.LAZY_MEMBER:
1929             return "toentity." ~ m ~ " = fromentity." ~ m ~ "();";
1930         case PropertyMemberKind.GETTER_MEMBER:
1931             return "toentity." ~ getterNameToSetterName(m) ~ "(fromentity." ~ m ~ "());";
1932         case PropertyMemberKind.PROPERTY_MEMBER:
1933             return "toentity." ~ m ~ " = fromentity." ~ m ~ ";";
1934         case PropertyMemberKind.UNSUPPORTED_MEMBER:
1935             assert(false, "Unsupported member kind " ~ T.stringof ~ "." ~ m);
1936     }
1937 }
1938 
1939 string getPropertyVariantWriteCode(T, string m)() {
1940     immutable memberType = getPropertyMemberType!(T,m)();
1941     immutable string nullValueCode = ColumnTypeSetNullCode[memberType];
1942     immutable string variantReadCode = ColumnTypeVariantReadCode[memberType];
1943     static if (getPropertyMemberKind!(T, m)() == PropertyMemberKind.GETTER_MEMBER) {
1944         return nullValueCode ~ "entity." ~ getterNameToSetterName(m) ~ "(" ~ variantReadCode ~ ");";
1945     } else {
1946         return nullValueCode ~ "entity." ~ m ~ " = " ~ variantReadCode ~ ";";
1947     }
1948 }
1949 
1950 string getPropertyVariantReadCode(T, string m)() {
1951     immutable memberType = getPropertyMemberType!(T,m)();
1952     immutable string propertyReadCode = getPropertyReadCode!(T,m)();
1953     return substituteParamTwice(ColumnTypePropertyToVariant[memberType], propertyReadCode);
1954 }
1955 
1956 
1957 static immutable string[] ColumnTypeConstructorCode = 
1958     [
1959      "new BooleanType()", // BOOL_TYPE, bool
1960      "new NumberType(2, false, SqlType.TINYINT)", //BYTE_TYPE,    // byte
1961      "new NumberType(4, false, SqlType.SMALLINT)", //SHORT_TYPE,   // short
1962      "new NumberType(9, false, SqlType.INTEGER)", //INT_TYPE,     // int
1963      "new NumberType(20, false, SqlType.BIGINT)", //LONG_TYPE,    // long
1964      "new NumberType(2, true, SqlType.TINYINT)", //UBYTE_TYPE,   // ubyte
1965      "new NumberType(4, true, SqlType.SMALLINT)", //USHORT_TYPE,  // ushort
1966      "new NumberType(9, true, SqlType.INTEGER)", //UINT_TYPE,    // uint
1967      "new NumberType(20, true, SqlType.BIGINT)", //ULONG_TYPE,   // ulong
1968      "new NumberType(2, false, SqlType.TINYINT)", //NULLABLE_BYTE_TYPE,  // Nullable!byte
1969      "new NumberType(4, false, SqlType.SMALLINT)", //NULLABLE_SHORT_TYPE, // Nullable!short
1970      "new NumberType(9, false, SqlType.INTEGER)", //NULLABLE_INT_TYPE,   // Nullable!int
1971      "new NumberType(20, false, SqlType.BIGINT)", //NULLABLE_LONG_TYPE,  // Nullable!long
1972      "new NumberType(2, true, SqlType.TINYINT)", //NULLABLE_UBYTE_TYPE, // Nullable!ubyte
1973      "new NumberType(4, true, SqlType.SMALLINT)", //NULLABLE_USHORT_TYPE,// Nullable!ushort
1974      "new NumberType(9, true, SqlType.INTEGER)", //NULLABLE_UINT_TYPE,  // Nullable!uint
1975      "new NumberType(20, true, SqlType.BIGINT)", //NULLABLE_ULONG_TYPE, // Nullable!ulong
1976      "new NumberType(7, false, SqlType.FLOAT)",//FLOAT_TYPE,   // float
1977      "new NumberType(14, false, SqlType.DOUBLE)",//DOUBLE_TYPE,   // double
1978      "new NumberType(7, false, SqlType.FLOAT)", //NULLABLE_FLOAT_TYPE, // Nullable!float
1979      "new NumberType(14, false, SqlType.DOUBLE)", //NULLABLE_DOUBLE_TYPE,// Nullable!double
1980      "new StringType()", //STRING_TYPE   // string
1981      "new StringType()", //NULLABLE_STRING_TYPE   // String
1982      "new DateTimeType()", //DATETIME_TYPE, // std.datetime.DateTime
1983      "new DateType()", //DATE_TYPE, // std.datetime.Date
1984      "new TimeType()", //TIME_TYPE, // std.datetime.TimeOfDay
1985      "new DateTimeType()", //NULLABLE_DATETIME_TYPE, // Nullable!std.datetime.DateTime
1986      "new DateType()", //NULLABLE_DATE_TYPE, // Nullable!std.datetime.Date
1987      "new TimeType()", //NULLABLE_TIME_TYPE, // Nullable!std.datetime.TimeOfDay
1988      "new ByteArrayBlobType()", //BYTE_ARRAY_TYPE, // byte[]
1989      "new UbyteArrayBlobType()", //UBYTE_ARRAY_TYPE, // ubyte[]
1990      ];
1991 
1992 string getColumnTypeName(T, string m, int length)() {
1993     immutable PropertyMemberType mt = getPropertyMemberType!(T,m);
1994     static if (mt == PropertyMemberType.STRING_TYPE || mt == PropertyMemberType.NULLABLE_STRING_TYPE) {
1995         return "new StringType(" ~ to!string(length) ~ ")";
1996     } else {
1997         return ColumnTypeConstructorCode[mt];
1998     }
1999 }
2000 
2001 static immutable string[] ColumnTypeDatasetReaderCode = 
2002     [
2003      "r.getBoolean(index)", //BOOL_TYPE,    // bool
2004      "r.getByte(index)", //BYTE_TYPE,    // byte
2005      "r.getShort(index)", //SHORT_TYPE,   // short
2006      "r.getInt(index)", //INT_TYPE,     // int
2007      "r.getLong(index)", //LONG_TYPE,    // long
2008      "r.getUbyte(index)", //UBYTE_TYPE,   // ubyte
2009      "r.getUshort(index)", //USHORT_TYPE,  // ushort
2010      "r.getUint(index)", //UINT_TYPE,    // uint
2011      "r.getUlong(index)", //ULONG_TYPE,   // ulong
2012      "Nullable!byte(r.getByte(index))", //NULLABLE_BYTE_TYPE,  // Nullable!byte
2013      "Nullable!short(r.getShort(index))", //NULLABLE_SHORT_TYPE, // Nullable!short
2014      "Nullable!int(r.getInt(index))", //NULLABLE_INT_TYPE,   // Nullable!int
2015      "Nullable!long(r.getLong(index))", //NULLABLE_LONG_TYPE,  // Nullable!long
2016      "Nullable!ubyte(r.getUbyte(index))", //NULLABLE_UBYTE_TYPE, // Nullable!ubyte
2017      "Nullable!ushort(r.getUshort(index))", //NULLABLE_USHORT_TYPE,// Nullable!ushort
2018      "Nullable!uint(r.getUint(index))", //NULLABLE_UINT_TYPE,  // Nullable!uint
2019      "Nullable!ulong(r.getUlong(index))", //NULLABLE_ULONG_TYPE, // Nullable!ulong
2020      "r.getFloat(index)",//FLOAT_TYPE,   // float
2021      "r.getDouble(index)",//DOUBLE_TYPE,   // double
2022      "Nullable!float(r.getFloat(index))", //NULLABLE_FLOAT_TYPE, // Nullable!float
2023      "Nullable!double(r.getDouble(index))", //NULLABLE_DOUBLE_TYPE,// Nullable!double
2024      "r.getString(index)", //STRING_TYPE   // string
2025      "r.getString(index)", //NULLABLE_STRING_TYPE   // String
2026      "r.getDateTime(index)", //DATETIME_TYPE, // std.datetime.DateTime
2027      "r.getDate(index)", //DATE_TYPE, // std.datetime.Date
2028      "r.getTime(index)", //TIME_TYPE, // std.datetime.TimeOfDay
2029      "Nullable!DateTime(r.getDateTime(index))", //NULLABLE_DATETIME_TYPE, // Nullable!std.datetime.DateTime
2030      "Nullable!Date(r.getDate(index))", //NULLABLE_DATE_TYPE, // Nullable!std.datetime.Date
2031      "Nullable!TimeOfDay(r.getTime(index))", //NULLABLE_TIME_TYPE, // Nullable!std.datetime.TimeOfDay
2032      "r.getBytes(index)", //BYTE_ARRAY_TYPE, // byte[]
2033      "r.getUbytes(index)", //UBYTE_ARRAY_TYPE, // ubyte[]
2034      ];
2035 
2036 string getColumnTypeDatasetReadCode(T, string m)() {
2037     return ColumnTypeDatasetReaderCode[getPropertyMemberType!(T,m)()];
2038 }
2039 
2040 static immutable string[] ColumnTypeVariantReadCode = 
2041     [
2042      "(value == null ? nv : value.get!(bool))", //BOOL_TYPE,    // bool
2043      "(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
2044      "(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
2045      "(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
2046      "(value == null ? nv : (value.convertsTo!(long) ? value.get!(long) : to!long(value.get!(ulong))))", //LONG_TYPE,    // long
2047      "(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
2048      "(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
2049      "(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
2050      "(value == null ? nv : (value.convertsTo!(ulong) ? value.get!(ulong) : to!ulong(value.get!(long))))", //ULONG_TYPE,   // ulong
2051      "(value == null ? nv : (value.convertsTo!(byte) ? value.get!(byte) : (value.convertsTo!(long) ? to!byte(value.get!(long)) : to!byte((value.get!(ulong))))))", //NULLABLE_BYTE_TYPE,  // Nullable!byte
2052      "(value == null ? nv : (value.convertsTo!(short) ? value.get!(short) : (value.convertsTo!(long) ? to!short(value.get!(long)) : to!short((value.get!(ulong))))))", //NULLABLE_SHORT_TYPE, // Nullable!short
2053      "(value == null ? nv : (value.convertsTo!(int) ? value.get!(int) : (value.convertsTo!(long) ? to!int(value.get!(long)) : to!int((value.get!(ulong))))))", //NULLABLE_INT_TYPE,   // Nullable!int
2054      "(value == null ? nv : (value.convertsTo!(long) ? value.get!(long) : to!long(value.get!(ulong))))", //NULLABLE_LONG_TYPE,  // Nullable!long
2055      "(value == null ? nv : (value.convertsTo!(ubyte) ? value.get!(ubyte) : (value.convertsTo!(ulong) ? to!ubyte(value.get!(ulong)) : to!ubyte((value.get!(long))))))", //NULLABLE_UBYTE_TYPE, // Nullable!ubyte
2056      "(value == null ? nv : (value.convertsTo!(ushort) ? value.get!(ushort) : (value.convertsTo!(ulong) ? to!ushort(value.get!(ulong)) : to!ushort((value.get!(long))))))", //NULLABLE_USHORT_TYPE,// Nullable!ushort
2057      "(value == null ? nv : (value.convertsTo!(uint) ? value.get!(uint) : (value.convertsTo!(ulong) ? to!uint(value.get!(ulong)) : to!uint((value.get!(long))))))", //NULLABLE_UINT_TYPE,  // Nullable!uint
2058      "(value == null ? nv : (value.convertsTo!(ulong) ? value.get!(ulong) : to!ulong(value.get!(long))))", //NULLABLE_ULONG_TYPE, // Nullable!ulong
2059      "(value == null ? nv : (value.convertsTo!(float) ? value.get!(float) : to!float(value.get!(double))))",//FLOAT_TYPE,   // float
2060      "(value == null ? nv : (value.convertsTo!(double) ? value.get!(double) : to!double(value.get!(double))))",//DOUBLE_TYPE,   // double
2061      "(value == null ? nv : (value.convertsTo!(float) ? value.get!(float) : to!float(value.get!(double))))", //NULLABLE_FLOAT_TYPE, // Nullable!float
2062      "(value == null ? nv : (value.convertsTo!(double) ? value.get!(double) : to!double(value.get!(double))))", //NULLABLE_DOUBLE_TYPE,// Nullable!double
2063      "(value == null ? nv : value.get!(string))", //STRING_TYPE   // string
2064      "(value == null ? nv : value.get!(string))", //NULLABLE_STRING_TYPE   // String
2065      "(value == null ? nv : value.get!(DateTime))", //DATETIME_TYPE, // std.datetime.DateTime
2066      "(value == null ? nv : value.get!(Date))", //DATE_TYPE, // std.datetime.Date
2067      "(value == null ? nv : value.get!(TimeOfDay))", //TIME_TYPE, // std.datetime.TimeOfDay
2068      "(value == null ? nv : value.get!(DateTime))", //NULLABLE_DATETIME_TYPE, // Nullable!std.datetime.DateTime
2069      "(value == null ? nv : value.get!(Date))", //NULLABLE_DATE_TYPE, // Nullable!std.datetime.Date
2070      "(value == null ? nv : value.get!(TimeOfDay))", //NULLABLE_TIME_TYPE, // Nullable!std.datetime.TimeOfDay
2071      "(value == null ? nv : value.get!(byte[]))", //BYTE_ARRAY_TYPE, // byte[]
2072      "(value == null ? nv : value.get!(ubyte[]))", //UBYTE_ARRAY_TYPE, // ubyte[]
2073      ];
2074 
2075 static immutable string[] DatasetWriteCode = 
2076     [
2077      "r.setBoolean(index, %s);", //BOOL_TYPE,    // bool
2078      "r.setByte(index, %s);", //BYTE_TYPE,    // byte
2079      "r.setShort(index, %s);", //SHORT_TYPE,   // short
2080      "r.setInt(index, %s);", //INT_TYPE,     // int
2081      "r.setLong(index, %s);", //LONG_TYPE,    // long
2082      "r.setUbyte(index, %s);", //UBYTE_TYPE,   // ubyte
2083      "r.setUshort(index, %s);", //USHORT_TYPE,  // ushort
2084      "r.setUint(index, %s);", //UINT_TYPE,    // uint
2085      "r.setUlong(index, %s);", //ULONG_TYPE,   // ulong
2086      "r.setByte(index, %s);", //NULLABLE_BYTE_TYPE,  // Nullable!byte
2087      "r.setShort(index, %s);", //NULLABLE_SHORT_TYPE, // Nullable!short
2088      "r.setInt(index, %s);", //NULLABLE_INT_TYPE,   // Nullable!int
2089      "r.setLong(index, %s);", //NULLABLE_LONG_TYPE,  // Nullable!long
2090      "r.setUbyte(index, %s);", //NULLABLE_UBYTE_TYPE, // Nullable!ubyte
2091      "r.setUshort(index, %s);", //NULLABLE_USHORT_TYPE,// Nullable!ushort
2092      "r.setUint(index, %s);", //NULLABLE_UINT_TYPE,  // Nullable!uint
2093      "r.setUlong(index, %s);", //NULLABLE_ULONG_TYPE, // Nullable!ulong
2094      "r.setFloat(index, %s);",//FLOAT_TYPE,   // float
2095      "r.setDouble(index, %s);",//DOUBLE_TYPE,   // double
2096      "r.setFloat(index, %s);", //NULLABLE_FLOAT_TYPE, // Nullable!float
2097      "r.setDouble(index, %s);", //NULLABLE_DOUBLE_TYPE,// Nullable!double
2098      "r.setString(index, %s);", //STRING_TYPE   // string
2099      "r.setString(index, %s);", //NULLABLE_STRING_TYPE   // String
2100      "r.setDateTime(index, %s);", //DATETIME_TYPE, // std.datetime.DateTime
2101      "r.setDate(index, %s);", //DATE_TYPE, // std.datetime.Date
2102      "r.setTime(index, %s);", //TIME_TYPE, // std.datetime.TimeOfDay
2103      "r.setDateTime(index, %s);", //NULLABLE_DATETIME_TYPE, // Nullable!std.datetime.DateTime
2104      "r.setDate(index, %s);", //NULLABLE_DATE_TYPE, // Nullable!std.datetime.Date
2105      "r.setTime(index, %s);", //NULLABLE_TIME_TYPE, // Nullable!std.datetime.TimeOfDay
2106      "r.setBytes(index, %s);", //BYTE_ARRAY_TYPE, // byte[]
2107      "r.setUbytes(index, %s);", //UBYTE_ARRAY_TYPE, // ubyte[]
2108      ];
2109 
2110 string getColumnTypeDatasetWriteCode(T, string m)() {
2111     alias typeof(__traits(getMember, T, m)) ti;
2112     immutable string isNullCode = getColumnTypeIsNullCode!(T,m)();
2113     immutable string readCode = getPropertyReadCode!(T,m)();
2114     immutable string setDataCode = DatasetWriteCode[getPropertyMemberType!(T,m)()];
2115     return "if (" ~ isNullCode ~ ") r.setNull(index); else " ~ substituteParam(setDataCode, readCode);
2116 }
2117 
2118 string getEmbeddedPropertyVariantWriteCode(T, string m, string className)() {
2119     immutable PropertyMemberKind kind = getPropertyMemberKind!(T, m)();
2120     final switch (kind) {
2121         case PropertyMemberKind.FIELD_MEMBER:
2122             return "entity." ~ m ~ " = (value == null ? null : value.get!(" ~ className ~ "));";
2123         case PropertyMemberKind.GETTER_MEMBER:
2124             return "entity." ~ getterNameToSetterName(m) ~ "(value == null ? null : value.get!(" ~ className ~ "));";
2125         case PropertyMemberKind.LAZY_MEMBER:
2126             return "entity." ~ m ~ " = (value == null ? null : value.get!(" ~ className ~ "));";
2127         case PropertyMemberKind.PROPERTY_MEMBER:
2128             return "entity." ~ m ~ " = (value == null ? null : value.get!(" ~ className ~ "));";
2129         case PropertyMemberKind.UNSUPPORTED_MEMBER:
2130             assert(false, "Unsupported member kind " ~ T.stringof ~ "." ~ m);
2131     }
2132 }
2133 
2134 string getCollectionPropertyVariantWriteCode(T, string m, string className)() {
2135     immutable PropertyMemberKind kind = getPropertyMemberKind!(T, m)();
2136     final switch (kind) {
2137         case PropertyMemberKind.FIELD_MEMBER:
2138             return "entity." ~ m ~ " = (value == null ? null : value.get!(" ~ className ~ "[]));";
2139         case PropertyMemberKind.GETTER_MEMBER:
2140             return "entity." ~ getterNameToSetterName(m) ~ "(value == null ? null : value.get!(" ~ className ~ "[]));";
2141         case PropertyMemberKind.LAZY_MEMBER:
2142             return "entity." ~ m ~ " = (value == null ? null : value.get!(" ~ className ~ "[]));";
2143         case PropertyMemberKind.PROPERTY_MEMBER:
2144             return "entity." ~ m ~ " = (value == null ? null : value.get!(" ~ className ~ "[]));";
2145         case PropertyMemberKind.UNSUPPORTED_MEMBER:
2146             assert(false, "Unsupported member kind " ~ T.stringof ~ "." ~ m);
2147     }
2148 }
2149 
2150 string getPropertyObjectWriteCode(T, string m, string className)() {
2151     immutable PropertyMemberKind kind = getPropertyMemberKind!(T, m)();
2152     final switch (kind) {
2153         case PropertyMemberKind.FIELD_MEMBER:
2154             return "entity." ~ m ~ " = cast(" ~ className ~ ")value;";
2155         case PropertyMemberKind.GETTER_MEMBER:
2156             return "entity." ~ getterNameToSetterName(m) ~ "(cast(" ~ className ~ ")value);";
2157         case PropertyMemberKind.PROPERTY_MEMBER:
2158             return "entity." ~ m ~ " = cast(" ~ className ~ ")value;";
2159         case PropertyMemberKind.LAZY_MEMBER:
2160             return "entity." ~ m ~ " = cast(" ~ className ~ ")value;";
2161         case PropertyMemberKind.UNSUPPORTED_MEMBER:
2162             assert(false, "Unsupported member kind " ~ T.stringof ~ "." ~ m);
2163     }
2164 }
2165 
2166 string getPropertyCollectionWriteCode(T, string m, string className)() {
2167     immutable PropertyMemberKind kind = getPropertyMemberKind!(T, m)();
2168     final switch (kind) {
2169         case PropertyMemberKind.FIELD_MEMBER:
2170             return "entity." ~ m ~ " = cast(" ~ className ~ "[])value;";
2171         case PropertyMemberKind.GETTER_MEMBER:
2172             return "entity." ~ getterNameToSetterName(m) ~ "(cast(" ~ className ~ "[])value);";
2173         case PropertyMemberKind.PROPERTY_MEMBER:
2174             return "entity." ~ m ~ " = cast(" ~ className ~ "[])value;";
2175         case PropertyMemberKind.LAZY_MEMBER:
2176             return "entity." ~ m ~ " = cast(" ~ className ~ "[])value;";
2177         case PropertyMemberKind.UNSUPPORTED_MEMBER:
2178             assert(false, "Unsupported member kind " ~ T.stringof ~ "." ~ m);
2179     }
2180 }
2181 
2182 string getLazyPropertyObjectWriteCode(T, string m)() {
2183     immutable PropertyMemberKind kind = getPropertyMemberKind!(T, m)();
2184     final switch (kind) {
2185         case PropertyMemberKind.FIELD_MEMBER:
2186             return "entity." ~ m ~ " = loader;";
2187         case PropertyMemberKind.GETTER_MEMBER:
2188             return "entity." ~ getterNameToSetterName(m) ~ "(loader);";
2189         case PropertyMemberKind.PROPERTY_MEMBER:
2190             return "entity." ~ m ~ " = loader;";
2191         case PropertyMemberKind.LAZY_MEMBER:
2192             return "entity." ~ m ~ " = loader;";
2193         case PropertyMemberKind.UNSUPPORTED_MEMBER:
2194             assert(false, "Unsupported member kind " ~ T.stringof ~ "." ~ m);
2195     }
2196 }
2197 
2198 string getLazyPropertyLoadedCode(T, string m)() {
2199     immutable PropertyMemberKind kind = getPropertyMemberKind!(T, m)();
2200     final switch (kind) {
2201         case PropertyMemberKind.FIELD_MEMBER:
2202             return "entity." ~ m ~ ".loaded";
2203         case PropertyMemberKind.GETTER_MEMBER:
2204             return "entity." ~ m ~ "().loaded";
2205         case PropertyMemberKind.PROPERTY_MEMBER:
2206             return "entity." ~ m ~ ".loaded";
2207         case PropertyMemberKind.LAZY_MEMBER:
2208             return "entity." ~ m ~ ".loaded";
2209         case PropertyMemberKind.UNSUPPORTED_MEMBER:
2210             assert(false, "Unsupported member kind " ~ T.stringof ~ "." ~ m);
2211     }
2212 }
2213 
2214 
2215 // TODO: minimize duplication of code in getXXXtoXXXPropertyDef
2216 
2217 /// generate source code for creation of OneToOne definition
2218 string getOneToOnePropertyDef(T, immutable string m)() {
2219     immutable string referencedEntityName = getPropertyReferencedEntityName!(T,m);
2220     immutable string referencedClassName = getPropertyReferencedClassName!(T,m);
2221     immutable string referencedPropertyName = getOneToOneReferencedPropertyName!(T,m);
2222     immutable string entityClassName = fullyQualifiedName!T;
2223     immutable string propertyName = getPropertyName!(T,m);
2224     static assert (propertyName != null, "Cannot determine property name for member " ~ m ~ " of type " ~ T.stringof);
2225     static assert (!hasOneOfMemberAnnotations!(T, m, Column, Id, Generated, Generator, ManyToOne, ManyToMany), entityClassName ~ "." ~ propertyName ~ ": OneToOne property cannot have Column, Id, Generated, Generator, ManyToOne, ManyToMany annotation");
2226     immutable bool isLazy = isLazyMember!(T,m);
2227     immutable string columnName = getJoinColumnName!(T, m);
2228     immutable length = getColumnLength!(T, m)();
2229     immutable bool hasNull = hasMemberAnnotation!(T,m, Null);
2230     immutable bool hasNotNull = hasMemberAnnotation!(T,m, NotNull);
2231     immutable bool nullable = hasNull ? true : (hasNotNull ? false : true); //canColumnTypeHoldNulls!(T.m)
2232     immutable string unique = quoteString(getUniqueIndexName!(T, m));
2233     immutable string typeName = "new EntityType(cast(immutable TypeInfo_Class)" ~ entityClassName ~ ".classinfo, \"" ~ entityClassName ~ "\")"; //getColumnTypeName!(T, m)();
2234     immutable string propertyReadCode = getPropertyReadCode!(T,m);
2235     immutable string datasetReadCode = null; //getColumnTypeDatasetReadCode!(T,m)();
2236     immutable string propertyWriteCode = null; //getPropertyWriteCode!(T,m)();
2237     immutable string datasetWriteCode = null; //getColumnTypeDatasetWriteCode!(T,m)();
2238     immutable string propertyVariantSetCode = getEmbeddedPropertyVariantWriteCode!(T, m, referencedClassName); // getPropertyVariantWriteCode!(T,m)();
2239     immutable string propertyVariantGetCode = "Variant(" ~ propertyReadCode ~ " is null ? null : " ~ propertyReadCode ~ ")"; //getPropertyVariantReadCode!(T,m)();
2240     immutable string propertyObjectSetCode = getPropertyObjectWriteCode!(T,m, referencedClassName); // getPropertyVariantWriteCode!(T,m)();
2241     immutable string propertyObjectGetCode = propertyReadCode; //getPropertyVariantReadCode!(T,m)();
2242     immutable string keyIsSetCode = null; //getColumnTypeKeyIsSetCode!(T,m)();
2243     immutable string isNullCode = propertyReadCode ~ " is null";
2244     immutable string copyFieldCode = getPropertyCopyCode!(T,m);
2245     //  pragma(msg, "property read: " ~ propertyReadCode);
2246     //  pragma(msg, "property write: " ~ propertyWriteCode);
2247     //  pragma(msg, "variant get: " ~ propertyVariantGetCode);
2248     immutable string readerFuncDef = "null";
2249     immutable string writerFuncDef = "null";
2250     immutable string getVariantFuncDef = 
2251         "\n" ~
2252             "function(Object obj) { \n" ~ 
2253             "    " ~ entityClassName ~ " entity = cast(" ~ entityClassName ~ ")obj; \n" ~
2254             "    return " ~ propertyVariantGetCode ~ "; \n" ~
2255             " }\n";
2256     immutable string setVariantFuncDef = 
2257         "\n" ~
2258             "function(Object obj, Variant value) { \n" ~ 
2259             "    " ~ entityClassName ~ " entity = cast(" ~ entityClassName ~ ")obj; \n" ~
2260             "    " ~ propertyVariantSetCode ~ "\n" ~
2261             " }\n";
2262     immutable string keyIsSetFuncDef = "\n" ~
2263         "function(Object obj) { \n" ~ 
2264             "    return false;\n" ~
2265             " }\n";
2266     immutable string isNullFuncDef = "\n" ~
2267         "function(Object obj) { \n" ~ 
2268             "    " ~ entityClassName ~ " entity = cast(" ~ entityClassName ~ ")obj; \n" ~
2269             "    return " ~ isNullCode ~ ";\n" ~
2270             " }\n";
2271     immutable string getObjectFuncDef = 
2272         "\n" ~
2273             "function(Object obj) { \n" ~ 
2274             "    " ~ entityClassName ~ " entity = cast(" ~ entityClassName ~ ")obj; \n" ~
2275             "    assert(entity !is null);\n" ~
2276             "    return " ~ propertyObjectGetCode ~ "; \n" ~
2277             " }\n";
2278     immutable string setObjectFuncDef = 
2279         "\n" ~
2280             "function(Object obj, Object value) { \n" ~ 
2281             "    " ~ entityClassName ~ " entity = cast(" ~ entityClassName ~ ")obj; \n" ~
2282             "    " ~ propertyObjectSetCode ~ "\n" ~
2283             " }\n";
2284     immutable string copyFuncDef = 
2285         "\n" ~
2286             "function(Object to, Object from) { \n" ~ 
2287             "    " ~ entityClassName ~ " toentity = cast(" ~ entityClassName ~ ")to; \n" ~
2288             "    " ~ entityClassName ~ " fromentity = cast(" ~ entityClassName ~ ")from; \n" ~
2289             "    " ~ copyFieldCode ~ "\n" ~
2290             " }\n";
2291     immutable string getCollectionFuncDef = "null";
2292     immutable string setCollectionFuncDef = "null";
2293     immutable string setObjectDelegateFuncDef = !isLazy ? "null" :
2294             "\n" ~
2295             "function(Object obj, Object delegate() loader) { \n" ~ 
2296             "    " ~ entityClassName ~ " entity = cast(" ~ entityClassName ~ ")obj; \n" ~
2297             "    " ~ getLazyPropertyObjectWriteCode!(T,m) ~ "\n" ~
2298             " }\n";
2299     immutable string setCollectionDelegateFuncDef = "null";
2300     immutable string isLoadedFuncDef = !isLazy ? "null" : 
2301             "\n" ~
2302             "function(Object obj) { \n" ~ 
2303             "    " ~ entityClassName ~ " entity = cast(" ~ entityClassName ~ ")obj; \n" ~
2304             "    return " ~ getLazyPropertyLoadedCode!(T,m) ~ ";\n" ~
2305             " }\n";
2306     
2307     return "    new PropertyInfo(" ~
2308             quoteString(propertyName) ~ ", " ~ 
2309             quoteString(columnName) ~ ", " ~ 
2310             typeName ~ ", " ~ 
2311             format("%s",length) ~ ", " ~ 
2312             "false, " ~ // id
2313             "false, " ~ // generated
2314             quoteBool(nullable) ~ ", " ~ 
2315             unique ~ ", " ~
2316             "RelationType.OneToOne, " ~
2317             quoteString(referencedEntityName)  ~ ", " ~ 
2318             quoteString(referencedPropertyName)  ~ ", " ~ 
2319             readerFuncDef ~ ", " ~
2320             writerFuncDef ~ ", " ~
2321             getVariantFuncDef ~ ", " ~
2322             setVariantFuncDef ~ ", " ~
2323             keyIsSetFuncDef ~ ", " ~
2324             isNullFuncDef ~ ", " ~
2325             copyFuncDef ~ ", " ~
2326             "null, " ~ // generatorFunc
2327             getObjectFuncDef ~ ", " ~
2328             setObjectFuncDef ~ ", " ~
2329             getCollectionFuncDef ~ ", " ~
2330             setCollectionFuncDef ~ ", " ~
2331             setObjectDelegateFuncDef ~ ", " ~
2332             setCollectionDelegateFuncDef ~ ", " ~
2333             isLoadedFuncDef ~ ", " ~
2334             quoteBool(isLazy) ~ ", " ~ // lazy
2335             "false" ~ // collection
2336             ")";
2337 }
2338 
2339 /// generate source code for creation of ManyToOne definition
2340 string getManyToOnePropertyDef(T, immutable string m)() {
2341     immutable string referencedEntityName = getPropertyReferencedEntityName!(T,m);
2342     immutable string referencedClassName = getPropertyReferencedClassName!(T,m);
2343     immutable string referencedPropertyName = getOneToOneReferencedPropertyName!(T,m);
2344     immutable string entityClassName = fullyQualifiedName!T;
2345     immutable string propertyName = getPropertyName!(T,m);
2346     static assert (propertyName != null, "Cannot determine property name for member " ~ m ~ " of type " ~ T.stringof);
2347     static assert (!hasOneOfMemberAnnotations!(T, m, Column, Id, Generated, Generator, OneToOne, ManyToMany), entityClassName ~ "." ~ propertyName ~ ": ManyToOne property cannot have Column, Id, Generated, Generator, OneToOne, ManyToMany annotation");
2348     immutable string columnName = applyDefault(getJoinColumnName!(T, m),camelCaseToUnderscoreDelimited(referencedEntityName) ~ "_fk");
2349     static assert (columnName != null, "ManyToOne property " ~ m ~ " has no JoinColumn name");
2350     immutable bool isLazy = isLazyMember!(T,m);
2351     immutable length = getColumnLength!(T, m);
2352     immutable bool hasNull = hasMemberAnnotation!(T,m, Null);
2353     immutable bool hasNotNull = hasMemberAnnotation!(T,m, NotNull);
2354     immutable bool nullable = hasNull ? true : (hasNotNull ? false : true); //canColumnTypeHoldNulls!(T.m)
2355     immutable string unique = quoteString(getUniqueIndexName!(T, m));
2356     immutable string typeName = "new EntityType(cast(immutable TypeInfo_Class)" ~ entityClassName ~ ".classinfo, \"" ~ entityClassName ~ "\")"; //getColumnTypeName!(T, m)();
2357     immutable string propertyReadCode = getPropertyReadCode!(T,m)();
2358     immutable string datasetReadCode = null; //getColumnTypeDatasetReadCode!(T,m)();
2359     immutable string propertyWriteCode = null; //getPropertyWriteCode!(T,m)();
2360     immutable string datasetWriteCode = null; //getColumnTypeDatasetWriteCode!(T,m)();
2361     immutable string propertyVariantSetCode = getEmbeddedPropertyVariantWriteCode!(T, m, referencedClassName); // getPropertyVariantWriteCode!(T,m)();
2362     immutable string propertyVariantGetCode = "Variant(" ~ propertyReadCode ~ " is null ? null : " ~ propertyReadCode ~ ")"; //getPropertyVariantReadCode!(T,m)();
2363     immutable string propertyObjectSetCode = getPropertyObjectWriteCode!(T,m, referencedClassName); // getPropertyVariantWriteCode!(T,m)();
2364     immutable string propertyObjectGetCode = propertyReadCode; //getPropertyVariantReadCode!(T,m)();
2365     immutable string keyIsSetCode = null; //getColumnTypeKeyIsSetCode!(T,m)();
2366     immutable string isNullCode = propertyReadCode ~ " is null";
2367     immutable string copyFieldCode = getPropertyCopyCode!(T,m);
2368     //  pragma(msg, "property read: " ~ propertyReadCode);
2369     //  pragma(msg, "property write: " ~ propertyWriteCode);
2370     //  pragma(msg, "variant get: " ~ propertyVariantGetCode);
2371     immutable string readerFuncDef = "null";
2372     immutable string writerFuncDef = "null";
2373     immutable string getVariantFuncDef = 
2374         "\n" ~
2375             "function(Object obj) { \n" ~ 
2376             "    " ~ entityClassName ~ " entity = cast(" ~ entityClassName ~ ")obj; \n" ~
2377             "    return " ~ propertyVariantGetCode ~ "; \n" ~
2378             " }\n";
2379     immutable string setVariantFuncDef = 
2380         "\n" ~
2381             "function(Object obj, Variant value) { \n" ~ 
2382             "    " ~ entityClassName ~ " entity = cast(" ~ entityClassName ~ ")obj; \n" ~
2383             "    " ~ propertyVariantSetCode ~ "\n" ~
2384             " }\n";
2385     immutable string keyIsSetFuncDef = "\n" ~
2386         "function(Object obj) { \n" ~ 
2387             "    return false;\n" ~
2388             " }\n";
2389     immutable string isNullFuncDef = "\n" ~
2390         "function(Object obj) { \n" ~ 
2391             "    " ~ entityClassName ~ " entity = cast(" ~ entityClassName ~ ")obj; \n" ~
2392             "    return " ~ isNullCode ~ ";\n" ~
2393             " }\n";
2394     immutable string getObjectFuncDef = 
2395         "\n" ~
2396             "function(Object obj) { \n" ~ 
2397             "    //writeln(\"Inside getObjectFunc\"); \n" ~
2398             "    " ~ entityClassName ~ " entity = cast(" ~ entityClassName ~ ")obj; \n" ~
2399             "    assert(entity !is null);\n" ~
2400             "    Object res = " ~ propertyObjectGetCode ~ "; \n" ~
2401             "    //writeln(res is null ? \"obj is null\" : \"obj is not null\"); \n" ~
2402             "    return res; \n" ~
2403             " }\n";
2404     immutable string setObjectFuncDef = 
2405         "\n" ~
2406             "function(Object obj, Object value) { \n" ~ 
2407             "    " ~ entityClassName ~ " entity = cast(" ~ entityClassName ~ ")obj; \n" ~
2408             "    " ~ propertyObjectSetCode ~ "\n" ~
2409             " }\n";
2410     immutable string copyFuncDef = 
2411         "\n" ~
2412             "function(Object to, Object from) { \n" ~ 
2413             "    " ~ entityClassName ~ " toentity = cast(" ~ entityClassName ~ ")to; \n" ~
2414             "    " ~ entityClassName ~ " fromentity = cast(" ~ entityClassName ~ ")from; \n" ~
2415             "    " ~ copyFieldCode ~ "\n" ~
2416             " }\n";
2417     immutable string getCollectionFuncDef = "null";
2418     immutable string setCollectionFuncDef = "null";
2419     immutable string setObjectDelegateFuncDef = !isLazy ? "null" :
2420         "\n" ~
2421         "function(Object obj, Object delegate() loader) { \n" ~ 
2422             "    " ~ entityClassName ~ " entity = cast(" ~ entityClassName ~ ")obj; \n" ~
2423             "    " ~ getLazyPropertyObjectWriteCode!(T,m) ~ "\n" ~
2424             " }\n";
2425     immutable string setCollectionDelegateFuncDef = "null";
2426     immutable string isLoadedFuncDef = !isLazy ? "null" : 
2427     "\n" ~
2428         "function(Object obj) { \n" ~ 
2429             "    " ~ entityClassName ~ " entity = cast(" ~ entityClassName ~ ")obj; \n" ~
2430             "    return " ~ getLazyPropertyLoadedCode!(T,m) ~ ";\n" ~
2431             " }\n";
2432 
2433     return "    new PropertyInfo(" ~
2434             quoteString(propertyName) ~ ", " ~ 
2435             quoteString(columnName) ~ ", " ~ 
2436             typeName ~ ", " ~ 
2437             format("%s",length) ~ ", " ~ 
2438             "false, " ~ // id
2439             "false, " ~ // generated
2440             quoteBool(nullable) ~ ", " ~ 
2441             unique ~ ", " ~
2442             "RelationType.ManyToOne, " ~
2443             quoteString(referencedEntityName)  ~ ", " ~ 
2444             quoteString(referencedPropertyName)  ~ ", " ~ 
2445             readerFuncDef ~ ", " ~
2446             writerFuncDef ~ ", " ~
2447             getVariantFuncDef ~ ", " ~
2448             setVariantFuncDef ~ ", " ~
2449             keyIsSetFuncDef ~ ", " ~
2450             isNullFuncDef ~ ", " ~
2451             copyFuncDef ~ ", " ~
2452             "null, " ~ // generatorFunc
2453             getObjectFuncDef ~ ", " ~
2454             setObjectFuncDef ~ ", " ~
2455             getCollectionFuncDef ~ ", " ~
2456             setCollectionFuncDef ~ ", " ~
2457             setObjectDelegateFuncDef ~ ", " ~
2458             setCollectionDelegateFuncDef ~ ", " ~
2459             isLoadedFuncDef ~ ", " ~
2460             quoteBool(isLazy) ~ ", " ~ // lazy
2461             "false" ~ // collection
2462             ")";
2463 }
2464 
2465 /// generate source code for creation of OneToMany definition
2466 string getOneToManyPropertyDef(T, immutable string m)() {
2467     immutable string referencedEntityName = getPropertyReferencedEntityName!(T,m);
2468     immutable string referencedClassName = getPropertyReferencedClassName!(T,m);
2469     immutable string referencedPropertyName = getOneToManyReferencedPropertyName!(T,m);
2470     static assert (referencedPropertyName != null, "OneToMany should have referenced property name parameter");
2471     immutable string entityClassName = fullyQualifiedName!T;
2472     immutable string propertyName = getPropertyName!(T,m)();
2473     static assert (propertyName != null, "Cannot determine property name for member " ~ m ~ " of type " ~ T.stringof);
2474     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");
2475     immutable string columnName = getJoinColumnName!(T, m)();
2476     immutable bool isCollection = isCollectionMember!(T,m);
2477     static assert (isCollection, "OneToMany property " ~ m ~ " should be array of objects or LazyCollection");
2478     static assert (columnName == null, "OneToMany property " ~ m ~ " should not have JoinColumn name");
2479     immutable bool isLazy = isLazyMember!(T,m) || isLazyCollectionMember!(T,m);
2480     immutable length = getColumnLength!(T, m);
2481     immutable bool hasNull = hasMemberAnnotation!(T,m, Null);
2482     immutable bool hasNotNull = hasMemberAnnotation!(T,m, NotNull);
2483     immutable bool nullable = hasNull ? true : (hasNotNull ? false : true); //canColumnTypeHoldNulls!(T.m)
2484     immutable string unique = quoteString(getUniqueIndexName!(T, m));
2485     immutable string typeName = "new EntityType(cast(immutable TypeInfo_Class)" ~ entityClassName ~ ".classinfo, \"" ~ entityClassName ~ "\")"; //getColumnTypeName!(T, m)();
2486     immutable string propertyReadCode = getPropertyReadCode!(T,m)();
2487     immutable string datasetReadCode = null; //getColumnTypeDatasetReadCode!(T,m)();
2488     immutable string propertyWriteCode = null; //getPropertyWriteCode!(T,m)();
2489     immutable string datasetWriteCode = null; //getColumnTypeDatasetWriteCode!(T,m)();
2490     immutable string propertyVariantSetCode = getCollectionPropertyVariantWriteCode!(T, m, referencedClassName); // getPropertyVariantWriteCode!(T,m)();
2491     immutable string propertyVariantGetCode = "Variant(" ~ propertyReadCode ~ " is null ? null : " ~ propertyReadCode ~ ")"; //getPropertyVariantReadCode!(T,m)();
2492     //pragma(msg, "propertyVariantGetCode: " ~ propertyVariantGetCode);
2493     //pragma(msg, "propertyVariantSetCode: " ~ propertyVariantSetCode);
2494     immutable string propertyObjectSetCode = getPropertyCollectionWriteCode!(T,m, referencedClassName); // getPropertyVariantWriteCode!(T,m)();
2495     immutable string propertyObjectGetCode = propertyReadCode; //getPropertyVariantReadCode!(T,m)();
2496     immutable string keyIsSetCode = null; //getColumnTypeKeyIsSetCode!(T,m)();
2497     immutable string isNullCode = propertyReadCode ~ " is null";
2498     immutable string copyFieldCode = getPropertyCopyCode!(T,m);
2499     //  pragma(msg, "property read: " ~ propertyReadCode);
2500     //  pragma(msg, "property write: " ~ propertyWriteCode);
2501     //  pragma(msg, "variant get: " ~ propertyVariantGetCode);
2502     immutable string readerFuncDef = "null";
2503     immutable string writerFuncDef = "null";
2504     immutable string getVariantFuncDef = 
2505         "\n" ~
2506             "function(Object obj) { \n" ~ 
2507             "    " ~ entityClassName ~ " entity = cast(" ~ entityClassName ~ ")obj; \n" ~
2508             "    return " ~ propertyVariantGetCode ~ "; \n" ~
2509             " }\n";
2510     immutable string setVariantFuncDef = 
2511         "\n" ~
2512             "function(Object obj, Variant value) { \n" ~ 
2513             "    " ~ entityClassName ~ " entity = cast(" ~ entityClassName ~ ")obj; \n" ~
2514             "    " ~ propertyVariantSetCode ~ "\n" ~
2515             " }\n";
2516     immutable string keyIsSetFuncDef = "\n" ~
2517         "function(Object obj) { \n" ~ 
2518             "    return false;\n" ~
2519             " }\n";
2520     immutable string isNullFuncDef = "\n" ~
2521         "function(Object obj) { \n" ~ 
2522             "    " ~ entityClassName ~ " entity = cast(" ~ entityClassName ~ ")obj; \n" ~
2523             "    return " ~ isNullCode ~ ";\n" ~
2524             " }\n";
2525     immutable string getObjectFuncDef = "null";
2526     immutable string setObjectFuncDef = "null";
2527     immutable string copyFuncDef = 
2528         "\n" ~
2529             "function(Object to, Object from) { \n" ~ 
2530             "    " ~ entityClassName ~ " toentity = cast(" ~ entityClassName ~ ")to; \n" ~
2531             "    " ~ entityClassName ~ " fromentity = cast(" ~ entityClassName ~ ")from; \n" ~
2532             "    " ~ copyFieldCode ~ "\n" ~
2533             " }\n";
2534     immutable string getCollectionFuncDef = 
2535         "\n" ~
2536         "function(Object obj) { \n" ~ 
2537             "    " ~ entityClassName ~ " entity = cast(" ~ entityClassName ~ ")obj; \n" ~
2538             "    assert(entity !is null);\n" ~
2539             "    return cast(Object[])" ~ propertyObjectGetCode ~ "; \n" ~
2540             " }\n";
2541     immutable string setCollectionFuncDef = 
2542         "\n" ~
2543         "function(Object obj, Object[] value) { \n" ~ 
2544             "    " ~ entityClassName ~ " entity = cast(" ~ entityClassName ~ ")obj; \n" ~
2545             "    " ~ propertyObjectSetCode ~ "\n" ~
2546             " }\n";
2547     immutable string setObjectDelegateFuncDef = "null";
2548     immutable string setCollectionDelegateFuncDef = !isLazy ? "null" :
2549     "\n" ~
2550         "function(Object obj, Object[] delegate() loader) { \n" ~ 
2551             "    " ~ entityClassName ~ " entity = cast(" ~ entityClassName ~ ")obj; \n" ~
2552             "    " ~ getLazyPropertyObjectWriteCode!(T,m) ~ "\n" ~
2553             " }\n";
2554     immutable string isLoadedFuncDef = !isLazy ? "null" : 
2555     "\n" ~
2556         "function(Object obj) { \n" ~ 
2557             "    " ~ entityClassName ~ " entity = cast(" ~ entityClassName ~ ")obj; \n" ~
2558             "    return " ~ getLazyPropertyLoadedCode!(T,m) ~ ";\n" ~
2559             " }\n";
2560 
2561     return "    new PropertyInfo(" ~
2562             quoteString(propertyName) ~ ", " ~ 
2563             quoteString(columnName) ~ ", " ~ 
2564             typeName ~ ", " ~ 
2565             format("%s",length) ~ ", " ~ 
2566             "false"  ~ ", " ~ // id
2567             "false"  ~ ", " ~ // generated
2568             quoteBool(nullable) ~ ", " ~ 
2569             unique ~ ", " ~
2570             "RelationType.OneToMany, " ~
2571             quoteString(referencedEntityName)  ~ ", " ~ 
2572             quoteString(referencedPropertyName)  ~ ", " ~ 
2573             readerFuncDef ~ ", " ~
2574             writerFuncDef ~ ", " ~
2575             getVariantFuncDef ~ ", " ~
2576             setVariantFuncDef ~ ", " ~
2577             keyIsSetFuncDef ~ ", " ~
2578             isNullFuncDef ~ ", " ~
2579             copyFuncDef ~ ", " ~
2580             "null, " ~ // generatorFunc
2581             getObjectFuncDef ~ ", " ~
2582             setObjectFuncDef ~ ", " ~
2583             getCollectionFuncDef ~ ", " ~
2584             setCollectionFuncDef ~ ", " ~
2585             setObjectDelegateFuncDef ~ ", " ~
2586             setCollectionDelegateFuncDef ~ ", " ~
2587             isLoadedFuncDef ~ ", " ~
2588             quoteBool(isLazy) ~ ", " ~ // lazy
2589             "true" ~ // is collection
2590             ")";
2591 }
2592 
2593 /// generate source code for creation of ManyToMany definition
2594 string getManyToManyPropertyDef(T, immutable string m)() {
2595     immutable string referencedEntityName = getPropertyReferencedEntityName!(T,m);
2596     immutable string referencedClassName = getPropertyReferencedClassName!(T,m);
2597     immutable string entityClassName = fullyQualifiedName!T;
2598     immutable string propertyName = getPropertyName!(T,m);
2599     static assert (propertyName != null, "Cannot determine property name for member " ~ m ~ " of type " ~ T.stringof);
2600     static assert (!hasOneOfMemberAnnotations!(T, m, Column, Id, Generated, Generator, OneToOne, OneToMany), entityClassName ~ "." ~ propertyName ~ ": ManyToMany property cannot have Column, Id, Generated, Generator, OneToOne, OneToMany annotation");
2601     immutable string columnName = getJoinColumnName!(T, m);
2602     immutable string joinTableName = getJoinTableName!(T, m);
2603     immutable string joinColumn1 = getJoinTableColumn1!(T, m);
2604     immutable string joinColumn2 = getJoinTableColumn2!(T, m);
2605     immutable string joinTableCode = JoinTableInfo.generateJoinTableCode(joinTableName, joinColumn1, joinColumn2);
2606     immutable bool isCollection = isCollectionMember!(T,m);
2607     static assert (isCollection, "ManyToMany property " ~ m ~ " should be array of objects or LazyCollection");
2608     static assert (columnName == null, "ManyToMany property " ~ m ~ " should not have JoinColumn name");
2609     immutable bool isLazy = isLazyMember!(T,m) || isLazyCollectionMember!(T,m);
2610     immutable length = getColumnLength!(T, m);
2611     immutable bool hasNull = hasMemberAnnotation!(T,m, Null);
2612     immutable bool hasNotNull = hasMemberAnnotation!(T,m, NotNull);
2613     immutable bool nullable = hasNull ? true : (hasNotNull ? false : true); //canColumnTypeHoldNulls!(T.m)
2614     immutable string unique = quoteString(getUniqueIndexName!(T, m));
2615     immutable string typeName = "new EntityType(cast(immutable TypeInfo_Class)" ~ entityClassName ~ ".classinfo, \"" ~ entityClassName ~ "\")"; //getColumnTypeName!(T, m)();
2616     immutable string propertyReadCode = getPropertyReadCode!(T,m);
2617     immutable string datasetReadCode = null; //getColumnTypeDatasetReadCode!(T,m)();
2618     immutable string propertyWriteCode = null; //getPropertyWriteCode!(T,m)();
2619     immutable string datasetWriteCode = null; //getColumnTypeDatasetWriteCode!(T,m)();
2620     immutable string propertyVariantSetCode = getCollectionPropertyVariantWriteCode!(T, m, referencedClassName); // getPropertyVariantWriteCode!(T,m)();
2621     immutable string propertyVariantGetCode = "Variant(" ~ propertyReadCode ~ " is null ? null : " ~ propertyReadCode ~ ")"; //getPropertyVariantReadCode!(T,m)();
2622     //pragma(msg, "propertyVariantGetCode: " ~ propertyVariantGetCode);
2623     //pragma(msg, "propertyVariantSetCode: " ~ propertyVariantSetCode);
2624     immutable string propertyObjectSetCode = getPropertyCollectionWriteCode!(T,m, referencedClassName); // getPropertyVariantWriteCode!(T,m)();
2625     immutable string propertyObjectGetCode = propertyReadCode; //getPropertyVariantReadCode!(T,m)();
2626     immutable string keyIsSetCode = null; //getColumnTypeKeyIsSetCode!(T,m)();
2627     immutable string isNullCode = propertyReadCode ~ " is null";
2628     immutable string copyFieldCode = getPropertyCopyCode!(T,m);
2629     immutable string readerFuncDef = "null";
2630     immutable string writerFuncDef = "null";
2631     immutable string getVariantFuncDef = 
2632         "\n" ~
2633             "function(Object obj) { \n" ~ 
2634             "    " ~ entityClassName ~ " entity = cast(" ~ entityClassName ~ ")obj; \n" ~
2635             "    return " ~ propertyVariantGetCode ~ "; \n" ~
2636             " }\n";
2637     immutable string setVariantFuncDef = 
2638         "\n" ~
2639             "function(Object obj, Variant value) { \n" ~ 
2640             "    " ~ entityClassName ~ " entity = cast(" ~ entityClassName ~ ")obj; \n" ~
2641             "    " ~ propertyVariantSetCode ~ "\n" ~
2642             " }\n";
2643     immutable string keyIsSetFuncDef = "\n" ~
2644         "function(Object obj) { \n" ~ 
2645             "    return false;\n" ~
2646             " }\n";
2647     immutable string isNullFuncDef = "\n" ~
2648         "function(Object obj) { \n" ~ 
2649             "    " ~ entityClassName ~ " entity = cast(" ~ entityClassName ~ ")obj; \n" ~
2650             "    return " ~ isNullCode ~ ";\n" ~
2651             " }\n";
2652     immutable string getObjectFuncDef = "null";
2653     immutable string setObjectFuncDef = "null";
2654     immutable string copyFuncDef = 
2655         "\n" ~
2656             "function(Object to, Object from) { \n" ~ 
2657             "    " ~ entityClassName ~ " toentity = cast(" ~ entityClassName ~ ")to; \n" ~
2658             "    " ~ entityClassName ~ " fromentity = cast(" ~ entityClassName ~ ")from; \n" ~
2659             "    " ~ copyFieldCode ~ "\n" ~
2660             " }\n";
2661     immutable string getCollectionFuncDef = 
2662         "\n" ~
2663             "function(Object obj) { \n" ~ 
2664             "    " ~ entityClassName ~ " entity = cast(" ~ entityClassName ~ ")obj; \n" ~
2665             "    assert(entity !is null);\n" ~
2666             "    return cast(Object[])" ~ propertyObjectGetCode ~ "; \n" ~
2667             " }\n";
2668     immutable string setCollectionFuncDef = 
2669         "\n" ~
2670             "function(Object obj, Object[] value) { \n" ~ 
2671             "    " ~ entityClassName ~ " entity = cast(" ~ entityClassName ~ ")obj; \n" ~
2672             "    " ~ propertyObjectSetCode ~ "\n" ~
2673             " }\n";
2674     immutable string setObjectDelegateFuncDef = "null";
2675     immutable string setCollectionDelegateFuncDef = !isLazy ? "null" :
2676     "\n" ~
2677         "function(Object obj, Object[] delegate() loader) { \n" ~ 
2678             "    " ~ entityClassName ~ " entity = cast(" ~ entityClassName ~ ")obj; \n" ~
2679             "    " ~ getLazyPropertyObjectWriteCode!(T,m) ~ "\n" ~
2680             " }\n";
2681     immutable string isLoadedFuncDef = !isLazy ? "null" : 
2682     "\n" ~
2683         "function(Object obj) { \n" ~ 
2684             "    " ~ entityClassName ~ " entity = cast(" ~ entityClassName ~ ")obj; \n" ~
2685             "    return " ~ getLazyPropertyLoadedCode!(T,m) ~ ";\n" ~
2686             " }\n";
2687 
2688     return "    new PropertyInfo(" ~
2689             quoteString(propertyName) ~ ", " ~ 
2690             quoteString(columnName) ~ ", " ~ 
2691             typeName ~ ", " ~ 
2692             format("%s",length) ~ ", " ~ 
2693             "false"  ~ ", " ~ // id
2694             "false"  ~ ", " ~ // generated
2695             quoteBool(nullable) ~ ", " ~ 
2696             unique ~ ", " ~
2697             "RelationType.ManyToMany, " ~
2698             quoteString(referencedEntityName)  ~ ", " ~ 
2699             "null, " ~ //referencedPropertyName
2700             readerFuncDef ~ ", " ~
2701             writerFuncDef ~ ", " ~
2702             getVariantFuncDef ~ ", " ~
2703             setVariantFuncDef ~ ", " ~
2704             keyIsSetFuncDef ~ ", " ~
2705             isNullFuncDef ~ ", " ~
2706             copyFuncDef ~ ", " ~
2707             "null, " ~ // generatorFunc
2708             getObjectFuncDef ~ ", " ~
2709             setObjectFuncDef ~ ", " ~
2710             getCollectionFuncDef ~ ", " ~
2711             setCollectionFuncDef ~ ", " ~
2712             setObjectDelegateFuncDef ~ ", " ~
2713             setCollectionDelegateFuncDef ~ ", " ~
2714             isLoadedFuncDef ~ ", " ~
2715             quoteBool(isLazy) ~ ", " ~ // lazy
2716             "true" ~ ", " ~ // is collection
2717             joinTableCode ~
2718             ")";
2719 }
2720 
2721 /// generate source code for creation of Embedded definition
2722 string getEmbeddedPropertyDef(T, immutable string m)() {
2723     immutable string referencedEntityName = getPropertyEmbeddedEntityName!(T,m);
2724     immutable string referencedClassName = getPropertyEmbeddedClassName!(T,m);
2725     immutable string entityClassName = fullyQualifiedName!T;
2726     immutable string propertyName = getPropertyName!(T,m);
2727     static assert (propertyName != null, "Cannot determine property name for member " ~ m ~ " of type " ~ T.stringof);
2728     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");
2729     immutable string columnName = getColumnName!(T, m);
2730     immutable length = getColumnLength!(T, m);
2731     immutable bool hasNull = hasMemberAnnotation!(T, m, Null);
2732     immutable bool hasNotNull = hasMemberAnnotation!(T, m, NotNull);
2733     immutable bool nullable = hasNull ? true : (hasNotNull ? false : true); //canColumnTypeHoldNulls!(T.m)
2734     immutable string unique = quoteString(getUniqueIndexName!(T, m));
2735     immutable string typeName = "new EntityType(cast(immutable TypeInfo_Class)" ~ entityClassName ~ ".classinfo, \"" ~ entityClassName ~ "\")"; //getColumnTypeName!(T, m)();
2736     immutable string propertyReadCode = getPropertyReadCode!(T,m);
2737     immutable string datasetReadCode = null; //getColumnTypeDatasetReadCode!(T,m)();
2738     immutable string propertyWriteCode = null; //getPropertyWriteCode!(T,m)();
2739     immutable string datasetWriteCode = null; //getColumnTypeDatasetWriteCode!(T,m)();
2740     immutable string propertyVariantSetCode = getEmbeddedPropertyVariantWriteCode!(T, m, referencedClassName); // getPropertyVariantWriteCode!(T,m)();
2741     immutable string propertyVariantGetCode = "Variant(" ~ propertyReadCode ~ " is null ? null : " ~ propertyReadCode ~ ")"; //getPropertyVariantReadCode!(T,m)();
2742     immutable string propertyObjectSetCode = getPropertyObjectWriteCode!(T,m, referencedClassName); // getPropertyVariantWriteCode!(T,m)();
2743     immutable string propertyObjectGetCode = propertyReadCode; //getPropertyVariantReadCode!(T,m)();
2744     immutable string keyIsSetCode = null; //getColumnTypeKeyIsSetCode!(T,m)();
2745     immutable string isNullCode = propertyReadCode ~ " is null";
2746     immutable string copyFieldCode = getPropertyCopyCode!(T,m);
2747     //  pragma(msg, "property read: " ~ propertyReadCode);
2748     //  pragma(msg, "property write: " ~ propertyWriteCode);
2749     //  pragma(msg, "variant get: " ~ propertyVariantGetCode);
2750     immutable string readerFuncDef = "null";
2751     immutable string writerFuncDef = "null";
2752     immutable string getVariantFuncDef = 
2753         "\n" ~
2754         "function(Object obj) { \n" ~ 
2755             "    " ~ entityClassName ~ " entity = cast(" ~ entityClassName ~ ")obj; \n" ~
2756             "    return " ~ propertyVariantGetCode ~ "; \n" ~
2757             " }\n";
2758     immutable string setVariantFuncDef = 
2759         "\n" ~
2760         "function(Object obj, Variant value) { \n" ~ 
2761             "    " ~ entityClassName ~ " entity = cast(" ~ entityClassName ~ ")obj; \n" ~
2762             "    " ~ propertyVariantSetCode ~ "\n" ~
2763             " }\n";
2764     immutable string keyIsSetFuncDef = "\n" ~
2765         "function(Object obj) { \n" ~ 
2766             "    return false;\n" ~
2767             " }\n";
2768     immutable string isNullFuncDef = "\n" ~
2769         "function(Object obj) { \n" ~ 
2770             "    " ~ entityClassName ~ " entity = cast(" ~ entityClassName ~ ")obj; \n" ~
2771             "    return " ~ isNullCode ~ ";\n" ~
2772             " }\n";
2773     immutable string getObjectFuncDef = 
2774         "\n" ~
2775             "function(Object obj) { \n" ~ 
2776             "    " ~ entityClassName ~ " entity = cast(" ~ entityClassName ~ ")obj; \n" ~
2777             "    assert(entity !is null);\n" ~
2778             "    return " ~ propertyObjectGetCode ~ "; \n" ~
2779             " }\n";
2780     immutable string setObjectFuncDef = 
2781         "\n" ~
2782             "function(Object obj, Object value) { \n" ~ 
2783             "    " ~ entityClassName ~ " entity = cast(" ~ entityClassName ~ ")obj; \n" ~
2784             "    " ~ propertyObjectSetCode ~ "\n" ~
2785             " }\n";
2786     immutable string copyFuncDef = 
2787         "\n" ~
2788             "function(Object to, Object from) { \n" ~ 
2789             "    " ~ entityClassName ~ " toentity = cast(" ~ entityClassName ~ ")to; \n" ~
2790             "    " ~ entityClassName ~ " fromentity = cast(" ~ entityClassName ~ ")from; \n" ~
2791             "    " ~ copyFieldCode ~ "\n" ~
2792             " }\n";
2793     
2794     return "    new PropertyInfo(" ~ 
2795             quoteString(propertyName) ~ ", " ~ 
2796             quoteString(columnName) ~ ", " ~ 
2797             typeName ~ ", " ~ 
2798             format("%s",length) ~ ", " ~ 
2799             "false, " ~ // id
2800             "false, " ~ // generated
2801             quoteBool(nullable) ~ ", " ~ 
2802             unique ~ ", " ~
2803             "RelationType.Embedded, " ~
2804             quoteString(referencedEntityName)  ~ ", " ~ 
2805             "null, \n" ~
2806             readerFuncDef ~ ", " ~
2807             writerFuncDef ~ ", " ~
2808             getVariantFuncDef ~ ", " ~
2809             setVariantFuncDef ~ ", " ~
2810             keyIsSetFuncDef ~ ", " ~
2811             isNullFuncDef ~ ", " ~
2812             copyFuncDef ~ ", " ~
2813             "null, " ~ // generatorFunc
2814             getObjectFuncDef ~ ", " ~
2815             setObjectFuncDef ~ 
2816             ")";
2817 }
2818 
2819 /// generate source code for creation of simple property definition
2820 string getSimplePropertyDef(T, immutable string m)() {
2821     //getPropertyReferencedEntityName(
2822     immutable string entityClassName = fullyQualifiedName!T;
2823     immutable string propertyName = getPropertyName!(T,m);
2824     static assert (propertyName != null, "Cannot determine property name for member " ~ m ~ " of type " ~ T.stringof);
2825     static assert (!hasOneOfMemberAnnotations!(T, m, ManyToOne, OneToOne, ManyToMany), entityClassName ~ "." ~ propertyName ~ ": simple property cannot have OneToOne, ManyToOne, or ManyToMany annotation");
2826     immutable bool isIdPropertyName = propertyName == "id";
2827     immutable bool isEmbeddableClass = hasAnnotation!(T, Embeddable);
2828     immutable bool classHasKeyField = hasAnyKeyPropertyAnnotation!T;
2829     immutable string generatorCode = getGeneratorCode!(T, m);
2830     immutable bool hasKeyAnnotation = hasMemberAnnotation!(T, m, Id) || hasMemberAnnotation!(T, m, Generated) || generatorCode != null;
2831     immutable bool isId = hasKeyAnnotation || (isIdPropertyName && !classHasKeyField && !isEmbeddableClass);
2832     immutable bool isGenerated = hasMemberAnnotation!(T, m, Generated) || (!hasKeyAnnotation && isId);
2833     immutable string columnName = getColumnName!(T, m);
2834     static assert(!isGenerated || generatorCode == null, T.stringof ~ "." ~ m ~ ": You cannot mix @Generated and @Generator for the same property");
2835     immutable length = getColumnLength!(T, m)();
2836     immutable bool hasNull = hasMemberAnnotation!(T,m,Null);
2837     immutable bool hasNotNull = hasMemberAnnotation!(T,m,NotNull);
2838     immutable bool nullable = hasNull ? true : (hasNotNull ? false : isColumnTypeNullableByDefault!(T, m)); //canColumnTypeHoldNulls!(T.m)
2839     immutable string unique = quoteString(getUniqueIndexName!(T, m));
2840     immutable string typeName = getColumnTypeName!(T, m, length);
2841     immutable string propertyReadCode = getPropertyReadCode!(T,m);
2842     immutable string datasetReadCode = getColumnTypeDatasetReadCode!(T,m);
2843     immutable string propertyWriteCode = getPropertyWriteCode!(T,m);
2844     immutable string datasetWriteCode = getColumnTypeDatasetWriteCode!(T,m);
2845     immutable string propertyVariantSetCode = getPropertyVariantWriteCode!(T,m);
2846     immutable string propertyVariantGetCode = getPropertyVariantReadCode!(T,m);
2847     immutable string keyIsSetCode = getColumnTypeKeyIsSetCode!(T,m);
2848     immutable string isNullCode = getColumnTypeIsNullCode!(T,m);
2849     immutable string copyFieldCode = getPropertyCopyCode!(T,m);
2850     immutable string readerFuncDef = "\n" ~
2851         "function(Object obj, DataSetReader r, int index) { \n" ~ 
2852             "    " ~ entityClassName ~ " entity = cast(" ~ entityClassName ~ ")obj; \n" ~
2853             "    " ~ propertyWriteCode ~ " \n" ~
2854             " }\n";
2855     immutable string writerFuncDef = "\n" ~
2856         "function(Object obj, DataSetWriter r, int index) { \n" ~ 
2857             "    " ~ entityClassName ~ " entity = cast(" ~ entityClassName ~ ")obj; \n" ~
2858             "    " ~ datasetWriteCode ~ " \n" ~
2859             " }\n";
2860     immutable string getVariantFuncDef = "\n" ~
2861         "function(Object obj) { \n" ~ 
2862             "    " ~ entityClassName ~ " entity = cast(" ~ entityClassName ~ ")obj; \n" ~
2863             "    return " ~ propertyVariantGetCode ~ "; \n" ~
2864             " }\n";
2865     immutable string setVariantFuncDef = "\n" ~
2866         "function(Object obj, Variant value) { \n" ~ 
2867             "    " ~ entityClassName ~ " entity = cast(" ~ entityClassName ~ ")obj; \n" ~
2868             "    " ~ propertyVariantSetCode ~ "\n" ~
2869             " }\n";
2870     immutable string keyIsSetFuncDef = "\n" ~
2871             "function(Object obj) { \n" ~ 
2872             "    " ~ entityClassName ~ " entity = cast(" ~ entityClassName ~ ")obj; \n" ~
2873             "    return " ~ keyIsSetCode ~ ";\n" ~
2874             " }\n";
2875     immutable string isNullFuncDef = "\n" ~
2876             "function(Object obj) { \n" ~ 
2877             "    " ~ entityClassName ~ " entity = cast(" ~ entityClassName ~ ")obj; \n" ~
2878             "    return " ~ isNullCode ~ ";\n" ~
2879             " }\n";
2880     immutable string copyFuncDef = 
2881             "\n" ~
2882             "function(Object to, Object from) { \n" ~ 
2883             "    " ~ entityClassName ~ " toentity = cast(" ~ entityClassName ~ ")to; \n" ~
2884             "    " ~ entityClassName ~ " fromentity = cast(" ~ entityClassName ~ ")from; \n" ~
2885             "    " ~ copyFieldCode ~ "\n" ~
2886             " }\n";
2887     immutable string generatorFuncDef = generatorCode is null ? "null" :
2888             "\n" ~
2889             "function(Connection conn, const PropertyInfo property) { \n" ~ 
2890             "    return Variant(" ~ generatorCode ~ ");\n" ~
2891             "}\n";
2892 
2893     static assert (typeName != null, "Cannot determine column type for member " ~ m ~ " of type " ~ T.stringof);
2894     return "    new PropertyInfo(" ~ 
2895             quoteString(propertyName) ~ ", " ~
2896             quoteString(columnName) ~ ", " ~ 
2897             typeName ~ ", " ~ 
2898             format("%s",length) ~ ", " ~ 
2899             quoteBool(isId)  ~ ", " ~ 
2900             quoteBool(isGenerated)  ~ ", " ~ 
2901             quoteBool(nullable) ~ ", " ~ 
2902             unique ~ ", " ~
2903             "RelationType.None, " ~ 
2904             "null, " ~ 
2905             "null, \n" ~
2906             readerFuncDef ~ ", " ~
2907             writerFuncDef ~ ", " ~
2908             getVariantFuncDef ~ ", " ~
2909             setVariantFuncDef ~ ", " ~
2910             keyIsSetFuncDef ~ ", " ~
2911             isNullFuncDef ~ ", " ~
2912             copyFuncDef ~ ", " ~
2913             generatorFuncDef ~
2914             ")";
2915 }
2916 
2917 /// creates "new PropertyInfo(...)" code to create property metadata for member m of class T
2918 string getPropertyDef(T, string m)() {
2919     immutable bool isObject = isObjectMember!(T, m);
2920     immutable bool isCollection = isCollectionMember!(T, m);
2921     immutable bool isEmbedded = isEmbeddedObjectMember!(T, m);
2922     immutable bool isOneToOne = hasMemberAnnotation!(T, m, OneToOne);
2923     immutable bool isManyToOne = hasMemberAnnotation!(T, m, ManyToOne);
2924     immutable bool isManyToMany = hasMemberAnnotation!(T, m, ManyToMany);
2925     immutable bool isOneToMany = hasMemberAnnotation!(T, m, OneToMany);
2926     immutable bool isSimple = isSupportedSimpleType!(T, m);
2927     static if (isSimple) {
2928         return getSimplePropertyDef!(T, m);
2929     } else static if (isObject) {
2930         static if (isOneToOne) {
2931             return getOneToOnePropertyDef!(T, m);
2932         } else static if (isEmbedded) {
2933             return getEmbeddedPropertyDef!(T, m);
2934         } else {
2935             // if no annotations on Object field, assume it is ManyToOne
2936             return getManyToOnePropertyDef!(T, m);
2937         }
2938 
2939     } else static if (isCollection) {
2940         static assert(!isEmbedded && !isOneToOne && !isManyToOne, "Collection object array or LazyCollection! cannot be marked as @Embedded, @OneToOne, or @ManyToOne");
2941         static if (isManyToMany) {
2942             return getManyToManyPropertyDef!(T, m);
2943         } else {
2944             // if no annotations on collection field, assume it is OneToMany
2945             return getOneToManyPropertyDef!(T, m);
2946         }
2947     }
2948 }
2949 
2950 string getEntityDef(T)() {
2951     string res;
2952     string generatedGettersSetters;
2953 
2954     string generatedEntityInfo;
2955     string generatedPropertyInfo;
2956 
2957     immutable string typeName = fullyQualifiedName!T;
2958     immutable bool isEntity = hasAnnotation!(T, Entity);
2959 
2960     //Don't require class level annotation. If no @Embeddable annotation, will treat as if there is @Entity annotation
2961     //static assert (hasOneOfAnnotations!(T, Entity, Embeddable), "Type " ~ typeName ~ " has neither @Entity nor @Embeddable annotation");
2962     static assert (!hasAnnotation!(T, Entity) || !hasAnnotation!(T, Embeddable), "Type " ~ typeName ~ " may not have both @Entity and @Embeddable at the same time");
2963     //pragma(msg, "Entity type name: " ~ typeName);
2964 
2965     immutable string entityName = getEntityName!T();
2966     immutable string tableName = getTableName!T();
2967 
2968     //pragma(msg, "preparing entity : " ~ entityName);
2969 
2970     static assert (entityName != null, "Type " ~ typeName ~ " has no Entity name specified");
2971     static assert (tableName != null, "Type " ~ typeName ~ " has no Table name specified");
2972 
2973     generatedEntityInfo ~= "new EntityInfo(";
2974     generatedEntityInfo ~= "\"" ~ entityName ~ "\", ";
2975     generatedEntityInfo ~= "\"" ~ tableName ~ "\", ";
2976     generatedEntityInfo ~= hasAnnotation!(T, Embeddable) ? "true," : "false,";
2977     generatedEntityInfo ~= "[\n";
2978 
2979     foreach (m; __traits(allMembers, T)) {
2980         //pragma(msg, m);
2981 
2982         static if (__traits(compiles, (typeof(__traits(getMember, T, m))))){
2983 
2984             // skip non-public members
2985             static if (__traits(getProtection, __traits(getMember, T, m)) == "public") {
2986 
2987                 alias typeof(__traits(getMember, T, m)) ti;
2988 
2989                 // hasDStructPropertyAnnotation!(T, m) &&
2990                 // automatically treat all public members of supported types as persistent
2991                 immutable bool typeSupported = (isSupportedSimpleType!(T, m) || isObjectMember!(T, m) || isCollectionMember!(T, m));
2992                 immutable bool isMainProp = isMainMemberForProperty!(T,m) && !hasMemberAnnotation!(T, m, Transient);
2993                 //pragma( msg, entityName ~ ":" ~ tableName ~ "." ~ m ~ ": typeSupported: " ~ (typeSupported ? "true" : "false") ~ " isMainProp: " ~ (isMainProp ? "true" : "false") )
2994                 static if (typeSupported && isMainProp) {
2995                     
2996                     immutable string propertyDef = getPropertyDef!(T, m)();
2997                     //pragma(msg, propertyDef);
2998 
2999                     if (generatedPropertyInfo != null)
3000                         generatedPropertyInfo ~= ",\n";
3001                     generatedPropertyInfo ~= propertyDef;
3002                 }
3003             }
3004         }
3005     }
3006     //pragma(msg, t);
3007     //pragma(msg, typeof(t));
3008 
3009     generatedEntityInfo ~= generatedPropertyInfo;
3010     generatedEntityInfo ~= "],";
3011     generatedEntityInfo ~= "" ~ typeName ~ ".classinfo";
3012     generatedEntityInfo ~= ")";
3013 
3014     //pragma(msg, "built entity : " ~ entityName);
3015 
3016     return generatedEntityInfo ~ "\n" ~ generatedGettersSetters;
3017 }
3018 
3019 template myPackageNamePrefix(alias T)
3020 {
3021     static if (is(typeof(__traits(parent, T))))
3022         enum parent = myPackageNamePrefix!(__traits(parent, T));
3023     else
3024         enum string parent = null;
3025 
3026     static if (T.stringof.startsWith("package "))
3027         enum myPackageNamePrefix = (parent ? parent ~ '.' : "") ~ T.stringof[8 .. $] ~ ".";
3028     else static if (parent)
3029         enum myPackageNamePrefix = parent;
3030     else
3031         enum myPackageNamePrefix = "";
3032 }
3033 
3034 string generateImportFor(T)() {
3035     static if (T.stringof.startsWith("module ")) {
3036         return "import " ~ fullyQualifiedName!T ~ ";\n";
3037     } else {
3038         return "import " ~ myPackageNamePrefix!T ~ moduleName!T ~ ";\n";
3039     }
3040 }
3041 
3042 string entityListDef(T ...)() {
3043     string res;
3044     string imp;
3045     foreach(t; T) {
3046         string impcode = "";
3047         static if (t.stringof.startsWith("module ")) {
3048             impcode = "import " ~ fullyQualifiedName!t ~ ";\n";
3049         } else {
3050             impcode = generateImportFor!(t);
3051         }
3052         if (indexOf(imp, impcode) < 0)
3053             imp ~= impcode;
3054     }
3055     foreach(t; T) {
3056         //pragma(msg, t);
3057         static if (t.stringof.startsWith("module ")) {
3058             //pragma(msg, "is module");
3059             //pragma(msg, "Module passed as schema parameter: " ~ t.stringof);
3060             //pragma(msg, __traits(allMembers, t));
3061             foreach(tt; __traits(allMembers, t)) {
3062                 //alias  ti;
3063                 //pragma(msg, "Module member: " ~ (__traits(getMember, t, tt)).stringof);
3064                 static if (__traits(compiles, isImplicitlyConvertible!((__traits(getMember, t, tt)), Object)) && isImplicitlyConvertible!((__traits(getMember, t, tt)), Object)) {
3065                     //pragma(msg, "checking member" ~ (__traits(getMember, t, tt)).stringof);
3066                     // import class metadata if class or any of its members has hibenrated annotation
3067                     static if (hasDStructClassOrPropertyAnnotation!(__traits(getMember, t, tt))) {
3068                         // class should not be marked as @Transient
3069                         static if (!hasAnnotation!(__traits(getMember, t, tt), Transient)) {
3070                             immutable string def = getEntityDef!(__traits(getMember, t, tt));
3071                             if (res.length > 0)
3072                                 res ~= ",\n";
3073                             res ~= def;
3074                         }
3075                     }
3076                 }
3077             }
3078         } else {
3079             //pragma(msg, "not module");
3080             static if (__traits(compiles, isImplicitlyConvertible!(t, Object)) && isImplicitlyConvertible!(t, Object)) {
3081 
3082                 static assert(!hasAnnotation!(t, Transient), "Class " ~ t.stringof ~ " has @Transient annotation and cannot be used in metadata");
3083 
3084                 // will be considered as @Entity if doesn't have @Embeddable annotation
3085                 immutable string def = getEntityDef!t;
3086 
3087                 //pragma(msg, def);
3088 
3089                 if (res.length > 0)
3090                     res ~= ",\n";
3091                 res ~= def;
3092             } else {
3093                     static assert(t.stringof ~ " cannot be passed as schema item");
3094             }
3095         }
3096     }
3097     string code = 
3098         "shared static this() {\n" ~
3099         imp ~ // imports
3100         "    //writeln(\"starting static initializer\");\n" ~
3101         "    entities = [\n" ~ res ~ "];\n" ~
3102         "    EntityInfo [string] map;\n" ~
3103         "    EntityInfo [TypeInfo_Class] typemap;\n" ~
3104         "    foreach(e; entities) {\n" ~
3105         "        map[e.name] = e;\n" ~
3106         "        typemap[cast(TypeInfo_Class)e.classInfo] = e;\n" ~
3107         "    }\n" ~
3108         "    entityMap = map;\n" ~
3109         "    classMap = typemap;\n" ~
3110         "    //writeln(\"updating referenced entities\");\n" ~
3111         "    foreach(e; entities) {\n" ~
3112 		"        //writefln( \"Entity:%s table:%s type:%s\", e.name, e.tableName, e.classInfo.name );\n" ~
3113         "        foreach(p; e._properties) {\n" ~
3114 		"            //writefln( \"\tproperty:%s column:%s ref-entityname:%s ref-propertyname:%s \", p.propertyName, p.columnName, p.referencedEntityName, p.referencedPropertyName );\n" ~
3115         "            if (p.referencedEntityName !is null) {\n" ~
3116         "                //writeln(\"embedded entity \" ~ p.referencedEntityName);\n" ~
3117         "                enforceHelper!MappingException((p.referencedEntityName in map) !is null, \"referenced entity not found in schema: \" ~ p.referencedEntityName);\n" ~
3118         "                p._referencedEntity = map[p.referencedEntityName];\n" ~
3119         "                if (p.referencedPropertyName !is null) {\n" ~
3120         "                    //writeln(\"\t\tembedded entity property name \" ~ p.referencedPropertyName );\n" ~
3121         "                    //writefln(\"\t\tembedded entity._propertyMap: %s \", p._referencedEntity._propertyMap );\n" ~
3122         "                    enforceHelper!MappingException((p.referencedPropertyName in p._referencedEntity._propertyMap) !is null, \"embedded entity property not found in schema: \" ~ p.referencedEntityName);\n" ~
3123         "                    p._referencedProperty = p._referencedEntity._propertyMap[p.referencedPropertyName];\n" ~
3124         "                }\n" ~
3125         "            }\n" ~
3126         "        }\n" ~
3127         "    }\n" ~
3128         "    //writeln(\"finished static initializer\");\n" ~
3129         "}";
3130     //pragma(msg, "built entity list");
3131     return code;
3132 }
3133 
3134 abstract class SchemaInfo : EntityMetaData {
3135 
3136     override @property size_t length() const {
3137         return getEntityCount();
3138     }
3139     override const(EntityInfo) opIndex(int index) const {
3140         return getEntity(index);
3141     }
3142     override const(EntityInfo) opIndex(string entityName) const {
3143         return findEntity(entityName);
3144     }
3145 
3146     override const(PropertyInfo) opIndex(string entityName, string propertyName) const {
3147         return findEntity(entityName).findProperty(propertyName);
3148     }
3149 
3150     override public Variant getPropertyValue(Object obj, string propertyName) const {
3151         return findEntityForObject(obj).getPropertyValue(obj, propertyName);
3152     }
3153 
3154     override public void setPropertyValue(Object obj, string propertyName, Variant value) const {
3155         findEntityForObject(obj).setPropertyValue(obj, propertyName, value);
3156     }
3157 
3158     private void appendCommaDelimitedList(ref string buf, string data) const {
3159         if (buf.length != 0)
3160             buf ~= ", ";
3161         buf ~= data;
3162     }
3163 
3164     public string getAllFieldListForUpdate(Dialect dialect, const EntityInfo ei, bool exceptKey = false) const {
3165         string query;
3166         foreach(pi; ei) {
3167             if (pi.key && exceptKey)
3168                 continue;
3169             if (pi.embedded) {
3170                 auto emei = pi.referencedEntity;
3171                 appendCommaDelimitedList(query, getAllFieldListForUpdate(dialect, emei, exceptKey));
3172             } else if (pi.oneToOne || pi.manyToOne) {
3173                 if (pi.columnName != null) {
3174                     // read FK column
3175                     appendCommaDelimitedList(query, dialect.quoteIfNeeded(pi.columnName) ~ "=?");
3176                 }
3177             } else if (pi.oneToMany || pi.manyToMany) {
3178                 // skip
3179             } else {
3180                 appendCommaDelimitedList(query, dialect.quoteIfNeeded(pi.columnName) ~ "=?");
3181             }
3182         }
3183         return query;
3184     }
3185     
3186     override public string getAllFieldList(Dialect dialect, const EntityInfo ei, bool exceptKey = false) const {
3187         string query;
3188         foreach(pi; ei) {
3189             if (pi.key && exceptKey)
3190                 continue;
3191             if (pi.embedded) {
3192                 auto emei = pi.referencedEntity;
3193                 appendCommaDelimitedList(query, getAllFieldList(dialect, emei, exceptKey));
3194             } else if (pi.oneToOne || pi.manyToOne) {
3195                 if (pi.columnName != null) {
3196                     // read FK column
3197                     appendCommaDelimitedList(query, dialect.quoteIfNeeded(pi.columnName));
3198                 }
3199             } else if (pi.oneToMany || pi.manyToMany) {
3200                 // skip
3201             } else {
3202                 appendCommaDelimitedList(query, dialect.quoteIfNeeded(pi.columnName));
3203             }
3204         }
3205         return query;
3206     }
3207     
3208     override public int getFieldCount(const EntityInfo ei, bool exceptKey) const {
3209         int count = 0;
3210         foreach(pi; ei) {
3211             if (pi.key && exceptKey)
3212                 continue;
3213             if (pi.embedded) {
3214                 auto emei = pi.referencedEntity;
3215                 count += getFieldCount(emei, exceptKey);
3216             } else if (pi.oneToOne || pi.manyToOne) {
3217                 if (pi.columnName != null) {
3218                     // read FK column
3219                     count++;
3220                 }
3221             } else if (pi.oneToMany || pi.manyToMany) {
3222                 // skip
3223             } else {
3224                 count++;
3225             }
3226         }
3227         return count;
3228     }
3229     
3230     public string getAllFieldPlaceholderList(const EntityInfo ei, bool exceptKey = false) const {
3231         string query;
3232         foreach(pi; ei) {
3233             if (pi.key && exceptKey)
3234                 continue;
3235             if (pi.embedded) {
3236                 auto emei = pi.referencedEntity;
3237                 appendCommaDelimitedList(query, getAllFieldPlaceholderList(emei));
3238             } else if (pi.oneToOne || pi.manyToOne) {
3239                 if (pi.columnName != null) {
3240                     // read FK column
3241                     appendCommaDelimitedList(query, "?");
3242                 }
3243             } else if (pi.oneToMany || pi.manyToMany) {
3244                 // skip
3245             } else {
3246                 appendCommaDelimitedList(query, "?");
3247             }
3248         }
3249         return query;
3250     }
3251     
3252     override public string getAllFieldList(Dialect dialect, string entityName, bool exceptKey) const {
3253         return getAllFieldList(dialect, findEntity(entityName), exceptKey);
3254     }
3255 
3256     override public int readAllColumns(Object obj, DataSetReader r, int startColumn) const {
3257         auto ei = findEntityForObject(obj);
3258         int columnCount = 0;
3259         foreach(pi; ei) {
3260             if (pi.embedded) {
3261                 auto emei = pi.referencedEntity;
3262                 Object em = emei.createEntity();
3263                 int columnsRead = readAllColumns(em, r, startColumn + columnCount);
3264                 pi.setObjectFunc(obj, em);
3265                 columnCount += columnsRead;
3266             } else if (pi.oneToOne || pi.manyToOne) {
3267                 if (pi.columnName !is null) {
3268                     Variant fk = r.getVariant(startColumn + columnCount);
3269                     // TODO: use FK
3270                     columnCount++;
3271                 } else {
3272                     // TODO: plan reading
3273                 }
3274             } else if (pi.oneToMany || pi.manyToMany) {
3275                 // skip
3276             } else {
3277                 pi.readFunc(obj, r, startColumn + columnCount);
3278                 columnCount++;
3279             }
3280         }
3281         return columnCount;
3282     }
3283 
3284     override public int writeAllColumns(Object obj, DataSetWriter w, int startColumn, bool exceptKey = false) const {
3285         auto ei = findEntityForObject(obj);
3286         //writeln(ei.name ~ ".writeAllColumns");
3287         int columnCount = 0;
3288         foreach(pi; ei) {
3289             if (pi.key && exceptKey)
3290                 continue;
3291             if (pi.embedded) {
3292                 auto emei = pi.referencedEntity;
3293                 //writeln("getting embedded entity " ~ emei.name);
3294                 assert(pi.getObjectFunc !is null, "No getObjectFunc defined for embedded entity " ~ emei.name);
3295                 Object em = pi.getObjectFunc(obj);
3296                 if (em is null)
3297                     em = emei.createEntity();
3298                 assert(em !is null, "embedded object is null");
3299                 //writeln("writing embedded entity " ~ emei.name);
3300                 int columnsWritten = writeAllColumns(em, w, startColumn + columnCount);
3301                 //writeln("written");
3302                 columnCount += columnsWritten;
3303             } else if (pi.oneToOne || pi.manyToOne) {
3304                 if (pi.columnName !is null) {
3305                     Object obj = pi.getObjectFunc(obj);
3306                     if (obj is null) {
3307                         w.setNull(startColumn + columnCount);
3308                     } else {
3309                         //writeln("setting ID column for property " ~ pi.entity.name ~ "." ~ pi.propertyName);
3310                         //if (pi.lazyLoad)
3311                         //    writeln("property has lazy loader");
3312                         //writeln("reading ID variant " ~ pi.propertyName ~ " from object");
3313                         Variant id = pi.referencedEntity.getKey(obj);
3314                         //writeln("setting parameter " ~ to!string(startColumn + columnCount));
3315                         w.setVariant(startColumn + columnCount, id);
3316                     }
3317                     columnCount++;
3318                 }
3319                 // skip
3320             } else if (pi.oneToMany || pi.manyToMany) {
3321                 // skip
3322             } else {
3323                 pi.writeFunc(obj, w, startColumn + columnCount);
3324                 columnCount++;
3325             }
3326         }
3327         return columnCount;
3328     }
3329 
3330     override public string generateFindAllForEntity(Dialect dialect, string entityName) const {
3331         auto ei = findEntity(entityName);
3332         return "SELECT " ~ getAllFieldList(dialect, ei) ~ " FROM " ~ dialect.quoteIfNeeded(ei.tableName);
3333     }
3334 
3335     override public string generateFindByPkForEntity(Dialect dialect, const EntityInfo ei) const {
3336         return "SELECT " ~ getAllFieldList(dialect, ei) ~ " FROM " ~ dialect.quoteIfNeeded(ei.tableName) ~ " WHERE " ~ dialect.quoteIfNeeded(ei.keyProperty.columnName) ~ " = ?";
3337     }
3338 
3339     override public string generateInsertAllFieldsForEntity(Dialect dialect, const EntityInfo ei) const {
3340         return "INSERT INTO " ~ dialect.quoteIfNeeded(ei.tableName) ~ "(" ~ getAllFieldList(dialect, ei) ~ ") VALUES (" ~ getAllFieldPlaceholderList(ei) ~ ")";
3341     }
3342 
3343     override public string generateInsertNoKeyForEntity(Dialect dialect, const EntityInfo ei) const {
3344         return "INSERT INTO " ~ dialect.quoteIfNeeded(ei.tableName) ~ "(" ~ getAllFieldList(dialect, ei, true) ~ ") VALUES (" ~ getAllFieldPlaceholderList(ei, true) ~ ")";
3345     }
3346 
3347     override public string generateUpdateForEntity(Dialect dialect, const EntityInfo ei) const {
3348         return "UPDATE " ~ dialect.quoteIfNeeded(ei.tableName) ~ " SET " ~ getAllFieldListForUpdate(dialect, ei, true) ~ " WHERE " ~ dialect.quoteIfNeeded(ei.getKeyProperty().columnName) ~ "=?";
3349     }
3350 
3351     override public string generateFindByPkForEntity(Dialect dialect, string entityName) const {
3352         return generateFindByPkForEntity(dialect, findEntity(entityName));
3353     }
3354 
3355     override public string generateInsertAllFieldsForEntity(Dialect dialect, string entityName) const {
3356         return generateInsertAllFieldsForEntity(dialect, findEntity(entityName));
3357     }
3358 }
3359 
3360 class SchemaInfoImpl(T...) : SchemaInfo {
3361     __gshared EntityInfo [string] entityMap;
3362     __gshared EntityInfo [] entities;
3363     __gshared EntityInfo [TypeInfo_Class] classMap;
3364 
3365     //import htestmain;
3366     //pragma(msg, entityListDef!(T)());
3367     mixin(entityListDef!(T)());
3368 
3369     override public int getEntityCount() const { return cast(int)entities.length; }
3370 
3371     override public const(EntityInfo[]) getEntities() const  { return entities; }
3372     override public const(EntityInfo[string]) getEntityMap() const  { return entityMap; }
3373     override public const(EntityInfo [TypeInfo_Class]) getClassMap() const  { return classMap; }
3374 
3375     override int opApply(int delegate(ref const EntityInfo) dg) const { 
3376         int result = 0; 
3377         for (int i = 0; i < entities.length; i++) { 
3378             result = dg(entities[i]); 
3379             if (result) break; 
3380         } 
3381         return result; 
3382     }
3383 
3384     override public const(EntityInfo) findEntity(string entityName) const  {
3385         enforceHelper!MappingException((entityName in entityMap) !is null, "Cannot find entity by name " ~ entityName);
3386         return entityMap[entityName]; 
3387     }
3388 
3389     override public const(EntityInfo) findEntity(TypeInfo_Class entityClass) const { 
3390         enforceHelper!MappingException((entityClass in classMap) !is null, "Cannot find entity by class " ~ entityClass.toString());
3391         return classMap[entityClass]; 
3392     }
3393 
3394     override public const(EntityInfo) getEntity(int entityIndex) const { 
3395         enforceHelper!MappingException(entityIndex >= 0 && entityIndex < entities.length, "Invalid entity index " ~ to!string(entityIndex));
3396         return entities[entityIndex]; 
3397     }
3398 
3399     override public Object createEntity(string entityName) const { 
3400         enforceHelper!MappingException((entityName in entityMap) !is null, "Cannot find entity by name " ~ entityName);
3401         return entityMap[entityName].createEntity(); 
3402     }
3403 
3404     override public const(EntityInfo) findEntityForObject(Object obj) const {
3405         enforceHelper!MappingException((obj.classinfo in classMap) !is null, "Cannot find entity by class " ~ obj.classinfo.toString());
3406         return classMap[obj.classinfo];
3407     }
3408     this() {
3409         // update entity._metadata reference
3410         foreach(e; entities) {
3411             e._metadata = this;
3412             int columnOffset = 0;
3413             foreach(p; e._properties) {
3414                 if (p.manyToMany) {
3415                     p.updateJoinTable();
3416                 }
3417                 p._columnOffset = columnOffset;
3418                 if (p.embedded) {
3419                     auto emei = p.referencedEntity;
3420                     columnOffset += e.metadata.getFieldCount(emei, false);
3421                 } else if (p.oneToOne || p.manyToOne) {
3422                     if (p.columnName != null) {
3423                         // read FK column
3424                         columnOffset++;
3425                     }
3426                 } else if( p.manyToMany || p.oneToMany ) {
3427                     //manyToMany and oneToMany do NOT have a column in the table.
3428                 } else {
3429                     columnOffset++;
3430                 }
3431             }
3432         }
3433     }
3434 }
3435 
3436 /// information about DB structure generated from DStruct entity metadata
3437 class DBInfo {
3438     Dialect dialect;
3439     EntityMetaData metaData;
3440     bool hasCircularRefs;
3441 
3442     this(Dialect dialect, EntityMetaData metaData) {
3443         this.dialect = dialect;
3444         this.metaData = metaData;
3445 
3446         foreach(entity; metaData) {
3447             if (!entity.embeddable)
3448                 add(new TableInfo(this, entity));
3449         }
3450         sortTables();
3451     }
3452 
3453     TableInfo[] tables;
3454     TableInfo[string] tableNameMap;
3455     TableInfo get(string tableName) {
3456         TableInfo res = find(tableName);
3457         enforceHelper!DStructException(res !is null, "table " ~ tableName ~ " is not found in schema");
3458         return res;
3459     }
3460     TableInfo find(string tableName) {
3461         if ((tableName in tableNameMap) is null)
3462             return null;
3463         return tableNameMap[tableName];
3464     }
3465     void add(TableInfo table) {
3466         enforceHelper!DStructException((table.tableName in tableNameMap) is null, "duplicate table " ~ table.tableName ~ " in schema");
3467         tables ~= table;
3468         tableNameMap[table.tableName] = table;
3469     }
3470     private static bool[string] arrayToMap(string[] keys) {
3471         bool[string] res;
3472         if (keys !is null) {
3473             foreach(key; keys)
3474                 res[key] = true;
3475         }
3476         return res;
3477     }
3478 
3479     /// drop and/or create tables and indexes in DB using specified connection
3480     void updateDBSchema(Connection conn, bool dropTables, bool createTables) {
3481         assert(dropTables || createTables);
3482         string[] existingTables = getExistingTables(conn);
3483         string[] batch;
3484         if (dropTables)
3485             batch ~= getDropTableSQL(existingTables);
3486         if (createTables)
3487             batch ~= getCreateTableSQL(dropTables ? null : existingTables);
3488         try {
3489             Statement stmt = conn.createStatement();
3490             scope(exit) stmt.close();
3491             foreach(sql; batch) {
3492                 stmt.executeUpdate(sql);
3493             }
3494         } catch (Throwable e) {
3495             throw new DStructException(e);
3496         }
3497     }
3498 
3499     string[] getExistingTables(Connection conn) {
3500         string[] res;
3501         try {
3502             Statement stmt = conn.createStatement();
3503             scope(exit) stmt.close();
3504             foreach(table; tables) {
3505                 string sql = dialect.getCheckTableExistsSQL(table.tableName);
3506                 ResultSet rs = stmt.executeQuery(sql);
3507                 scope(exit)rs.close();
3508                 if (rs.next())
3509                     res ~= table.tableName;
3510             }
3511         } catch (Throwable e) {
3512             throw new DStructException(e);
3513         }
3514         return res;
3515     }
3516     string[] getCreateTableSQL(string[] existingTables = null) {
3517         auto map = arrayToMap(existingTables);
3518         string[] res;
3519         foreach(table; tables) {
3520             if (existingTables is null || (table.tableName in map) is null)
3521                 res ~= table.getCreateTableSQL();
3522         }
3523         return res;
3524     }
3525     string[] getCreateIndexSQL(string[] existingTables = null) {
3526         auto map = arrayToMap(existingTables);
3527         string[] res;
3528         foreach(table; tables) {
3529             if (existingTables is null || (table.tableName in map) is null)
3530                 res ~= table.getCreateIndexSQL();
3531         }
3532         return res;
3533     }
3534     string[] getDropTableSQL(string[] existingTables = null) {
3535         auto map = arrayToMap(existingTables);
3536         string[] res;
3537         foreach(table; tables) {
3538             if (existingTables is null || (table.tableName in map) !is null) {
3539                 if (hasCircularRefs)
3540                     res ~= table.getDropIndexSQL();
3541                 res ~= table.getDropTableSQL();
3542             }
3543         }
3544         return res;
3545     }
3546     TableInfo opIndex(string tableName) {
3547         TableInfo ti = find(tableName);
3548         enforceHelper!DStructException(ti !is null, "Table " ~ tableName ~ " is not found in schema");
3549         return ti;
3550     }
3551     private static TableInfo[] addTableSorted(TableInfo[] list, TableInfo table) {
3552         TableInfo[] head;
3553         TableInfo[] tail;
3554         if (list.length == 0) {
3555             // trivial
3556             return [table];
3557         } else {
3558             foreach(ti; list) {
3559                 if (ti.references(table))
3560                     tail ~= ti;
3561                 else
3562                     head ~= ti;
3563             }
3564             return head ~ [table] ~ tail;
3565         }
3566     }
3567     private void sortTables() {
3568         TableInfo[] list;
3569         foreach(table; tables) {
3570             list = addTableSorted(list, table);
3571         }
3572         tables = list;
3573         hasCircularRefs = hasCircularReferences();
3574         if (hasCircularRefs)
3575             writeln("has circular references");
3576     }
3577     private bool hasCircularReferences() {
3578         for (int i=0; i<tables.length; i++)
3579             for (int j=i + 1; j<tables.length; j++)
3580                 if (tables[i].references(tables[j]))
3581                     return true;
3582         return false;
3583     }
3584 }
3585 
3586 /// information about table in DB
3587 class TableInfo {
3588     DBInfo schema;
3589     string tableName;
3590     const EntityInfo entity;
3591     const EntityInfo entity2;
3592     ColumnInfo[] columns;
3593     ColumnInfo[string] columnNameMap;
3594     IndexInfo[] indexes;
3595     const string pkDef;
3596 
3597     this(DBInfo schema, const EntityInfo entity, const EntityInfo entity2, const JoinTableInfo joinTable) {
3598         this.schema = schema;
3599         this.tableName = joinTable.tableName;
3600         this.entity = entity;
3601         this.entity2 = entity;
3602         ColumnInfo c1;
3603         ColumnInfo c2;
3604         assert(joinTable.column1 !is null);
3605         assert(joinTable.column2 !is null);
3606         assert(entity !is null);
3607         assert(entity2 !is null);
3608         assert(joinTable.thisEntity !is null);
3609         assert(joinTable.otherEntity !is null);
3610         if (joinTable.column1 < joinTable.column2) {
3611             c1 = new ColumnInfo(this, joinTable.column1, entity);
3612             c2 = new ColumnInfo(this, joinTable.column2, entity2);
3613         } else {
3614             c2 = new ColumnInfo(this, joinTable.column1, entity);
3615             c1 = new ColumnInfo(this, joinTable.column2, entity2);
3616         }
3617         addColumn(c1);
3618         addColumn(c2);
3619         pkDef = "PRIMARY KEY (" ~ schema.dialect.quoteIfNeeded(c1.columnName) ~ ", " ~ schema.dialect.quoteIfNeeded(c2.columnName) ~ "), " ~
3620             schema.dialect.getUniqueIndexItemSQL(tableName ~ "_reverse_index", [c2.columnName, c1.columnName]);
3621         addForeignKey(tableName, entity, joinTable.column1, null);
3622         addForeignKey(tableName, entity2, joinTable.column2, null);
3623     }
3624 
3625     ColumnInfo opIndex(string columnName) {
3626         ColumnInfo ti = find(columnName);
3627         enforceHelper!DStructException(ti !is null, "Column " ~ columnName ~ " is not found in table " ~ tableName);
3628         return ti;
3629     }
3630 
3631     ColumnInfo find(string columnName) {
3632         if ((columnName in columnNameMap) is null)
3633             return null;
3634         return columnNameMap[columnName];
3635     }
3636 
3637     private void appendColumns(const EntityInfo entity) {
3638         foreach(pi; entity) {
3639             if (pi.embedded) {
3640                 appendColumns(pi.referencedEntity);
3641             } else if (pi.simple || (pi.columnName !is null)) {
3642                 addColumn(new ColumnInfo(this, pi));
3643                 if (pi.simple && pi.uniqueIndex !is null) //pi.unique)
3644                     addUniqueColumnIndex(pi);
3645             } else if (pi.manyToMany) {
3646                 addJoinTable(pi);
3647             }
3648         }
3649     }
3650     this(DBInfo schema, const EntityInfo entity) {
3651         this.schema = schema;
3652         this.entity = entity;
3653         this.entity2 = null;
3654         this.tableName = entity.tableName;
3655         this.pkDef = null;
3656         appendColumns(entity);
3657     }
3658     void addJoinTable(const PropertyInfo pi) {
3659         assert(pi.referencedEntity !is null);
3660         assert(pi.joinTable !is null);
3661         TableInfo t = new TableInfo(schema, entity, pi.referencedEntity, pi.joinTable);
3662         TableInfo existing = schema.find(t.tableName);
3663         if (existing !is null) {
3664             enforceHelper!DStructException(t.getCreateTableSQL() == existing.getCreateTableSQL(), "JoinTable structure in " ~ entity.name ~ " and " ~ pi.referencedEntityName ~ " do not match");
3665         } else {
3666             schema.add(t);
3667         }
3668     }
3669     void addUniqueColumnIndex(const PropertyInfo pi) {
3670         assert(pi.columnName !is null);
3671         IndexInfo index = new IndexInfo(this, IndexType.Unique);
3672         index.indexName = pi.uniqueIndex;
3673         index.columnNames ~= pi.columnName;
3674         addIndex(index);
3675     }
3676     void addForeignKey(string thisTable, const EntityInfo otherEntity, string columnName, string uniqueIndex) {
3677         IndexInfo index = new IndexInfo(this, uniqueIndex is null ? IndexType.ForeignKey : IndexType.UniqueForeignKey);
3678         index.indexName = thisTable ~ "_" ~ columnName ~ "_index";
3679         index.columnNames ~= columnName;
3680         index.referencedTable = otherEntity.tableName;
3681         index.referencedColumnNames ~= otherEntity.getKeyProperty().columnName;
3682         addIndex(index);
3683     }
3684     void addForeignKey(const PropertyInfo pi) {
3685         assert(pi.columnName !is null);
3686         assert(pi.manyToOne || pi.oneToOne);
3687         addForeignKey(pi.entity.tableName, pi.referencedEntity, pi.columnName, pi.uniqueIndex);
3688     }
3689     private void addIndex(IndexInfo index) {
3690         // TODO: check duplicates
3691         indexes ~= index;
3692     }
3693     void addColumn(ColumnInfo column) {
3694         enforceHelper!DStructException((column.columnName in columnNameMap) is null, "duplicate column name " ~ tableName ~ "." ~ column.columnName ~ " in schema");
3695         columns ~= column;
3696         columnNameMap[column.columnName] = column;
3697         if (column.property !is null && (column.property.manyToOne || column.property.oneToOne)) {
3698             addForeignKey(column.property);
3699         }
3700     }
3701     string getCreateTableSQL() {
3702         string res;
3703         foreach(col; columns) {
3704             if (res.length > 0)
3705                 res ~= ", ";
3706             res ~= col.columnDefinition;
3707         }
3708         if (pkDef !is null)
3709             res ~= ", " ~ pkDef;
3710         return "CREATE TABLE " ~ schema.dialect.quoteIfNeeded(tableName) ~ " (" ~ res ~ ")";
3711     }
3712     string getDropTableSQL() {
3713         return "DROP TABLE IF EXISTS " ~ schema.dialect.quoteIfNeeded(tableName);
3714     }
3715     string[] getDropIndexSQL() {
3716         string[] res;
3717         foreach(index; indexes) {
3718             if (index.type == IndexType.ForeignKey || index.type == IndexType.UniqueForeignKey) {
3719                 res ~= index.getDropIndexSQL();
3720             }
3721         }
3722         return res;
3723     }
3724     string[] getCreateIndexSQL() {
3725         string[] res;
3726         foreach(index; indexes) {
3727             res ~= index.getCreateIndexSQL();
3728         }
3729         return res;
3730     }
3731     bool references(ref bool[string] visitedTables, TableInfo other) {
3732         visitedTables[tableName] = true;
3733         foreach(index; indexes) {
3734             if (index.type == IndexType.ForeignKey || index.type == IndexType.UniqueForeignKey) {
3735                 if (index.referencedTable == other.tableName)
3736                     return true;
3737                 if ((index.referencedTable in visitedTables) is null) {
3738                     // not yet visited
3739                     TableInfo t = schema.find(index.referencedTable);
3740                     enforceHelper!DStructException(t !is null, "Table " ~ index.referencedTable ~ " referenced in index " ~ index.indexName ~ " is not found in schema");
3741                     if (t.references(visitedTables, other))
3742                         return true;
3743                 }
3744             }
3745         }
3746         return false;
3747     }
3748     bool references(TableInfo other) {
3749         bool[string] visitedTables;
3750         return references(visitedTables, other);
3751     }
3752 }
3753 
3754 class ColumnInfo {
3755     TableInfo table;
3756     const PropertyInfo property;
3757     string columnName;
3758     string columnDefinition;
3759     this(TableInfo table, string columnName, const EntityInfo referencedEntity) {
3760         this.table = table;
3761         this.property = null;
3762         this.columnName = columnName;
3763         this.columnDefinition = table.schema.dialect.quoteIfNeeded(columnName) ~ " " ~ 
3764                 table.schema.dialect.getColumnTypeDefinition(null, referencedEntity.getKeyProperty());
3765     }
3766     this(TableInfo table, const PropertyInfo property) {
3767         this.table = table;
3768         this.property = property;
3769         this.columnName = property.columnName;
3770         assert(columnName !is null);
3771         if (property.manyToOne || property.oneToOne) {
3772             assert(property.columnName !is null);
3773             assert(property.referencedEntity !is null);
3774             this.columnDefinition = table.schema.dialect.quoteIfNeeded(property.columnName) ~ " " ~ table.schema.dialect.getColumnTypeDefinition(property, property.referencedEntity.getKeyProperty());
3775         } else {
3776             this.columnDefinition = table.schema.dialect.getColumnDefinition(property);
3777         }
3778     }
3779 }
3780 
3781 enum IndexType {
3782     Index,
3783     Unique,
3784     ForeignKey,
3785     UniqueForeignKey
3786 }
3787 
3788 class IndexInfo {
3789     TableInfo table;
3790     IndexType type;
3791     string indexName;
3792     string[] columnNames;
3793     string referencedTable;
3794     string[] referencedColumnNames;
3795     this(TableInfo table, IndexType type) {
3796         this.table = table;
3797         this.type = type;
3798     }
3799     string[] getDropIndexSQL() {
3800         final switch(type) {
3801             case IndexType.Unique:
3802             case IndexType.Index:
3803                 return [table.schema.dialect.getDropIndexSQL(table.tableName, indexName)];
3804             case IndexType.ForeignKey:
3805             case IndexType.UniqueForeignKey:
3806                 return [table.schema.dialect.getDropForeignKeySQL(table.tableName, indexName), 
3807                         table.schema.dialect.getDropIndexSQL(table.tableName, indexName)];
3808         }
3809     }
3810     string[] getCreateIndexSQL() {
3811         final switch(type) {
3812             case IndexType.Unique:
3813                 return [table.schema.dialect.getUniqueIndexSQL(table.tableName, indexName, columnNames)];
3814             case IndexType.Index:
3815                 return [table.schema.dialect.getIndexSQL(table.tableName, indexName, columnNames)];
3816             case IndexType.ForeignKey:
3817                 return [table.schema.dialect.getForeignKeySQL(table.tableName, indexName, columnNames, referencedTable, referencedColumnNames)];
3818             case IndexType.UniqueForeignKey:
3819                 return [table.schema.dialect.getUniqueIndexSQL(table.tableName, indexName, columnNames), 
3820                         table.schema.dialect.getForeignKeySQL(table.tableName, indexName, columnNames, referencedTable, referencedColumnNames)];
3821         }
3822     }
3823 }
3824 
3825 unittest {
3826 
3827     @Entity
3828     @Table("users")
3829     static class User {
3830         
3831         //@Id @Generated
3832         @Column("id_column")
3833         int id;
3834         
3835         @Column("name_column")
3836         string name;
3837         
3838         // no column name
3839         //@Column
3840         string flags;
3841         
3842         // annotated getter
3843         private string login;
3844         //@Column
3845         public string getLogin() { return login; }
3846         public void setLogin(string login) { this.login = login; }
3847         
3848         // no (), no column name
3849         //@Column
3850         int testColumn;
3851     }
3852     
3853     
3854     @Entity
3855     @Table("customer")
3856     static class Customer {
3857         //@Id @Generated
3858         //@Column
3859         int id;
3860         //@Column
3861         string name;
3862     }
3863 
3864 
3865     EntityInfo entity = new EntityInfo("user", "users",  false, [
3866                                                                  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)
3867                                                          ], null);
3868 
3869     assert(entity.properties.length == 1);
3870 
3871 
3872 //  immutable string info = getEntityDef!User();
3873 //  immutable string infos = entityListDef!(User, Customer)();
3874 
3875     EntityInfo ei = new EntityInfo("User", "users", false, [
3876                                                             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),
3877                                                             new PropertyInfo("name", "name_column", new StringType(), 0, false, false, false, null, RelationType.None, null, null, null, null, null, null, null, null, null),
3878                                                             new PropertyInfo("flags", "flags", new StringType(), 0, false, false, true, null, RelationType.None, null, null, null, null, null, null, null, null, null),
3879                                                             new PropertyInfo("login", "login", new StringType(), 0, false, false, true, null, RelationType.None, null, null, null, null, null, null, null, null, null),
3880                                                             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);
3881 
3882     //void function(User, DataSetReader, int) readFunc = function(User entity, DataSetReader reader, int index) { };
3883 
3884     assert(ei.findProperty("name").columnName == "name_column");
3885     assert(ei.getProperties()[0].columnName == "id_column");
3886     assert(ei.getProperty(2).propertyName == "flags");
3887     assert(ei.getPropertyCount == 5);
3888 
3889     EntityInfo[] entities3 =  [
3890                                new EntityInfo("User", "users", false, [
3891                                             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),
3892                                             new PropertyInfo("name", "name_column", new StringType(), 0, false, false, false, null, RelationType.None, null, null, null, null, null, null, null, null, null),
3893                                             new PropertyInfo("flags", "flags", new StringType(), 0, false, false, true, null, RelationType.None, null, null, null, null, null, null, null, null, null),
3894                                             new PropertyInfo("login", "login", new StringType(), 0, false, false, true, null, RelationType.None, null, null, null, null, null, null, null, null, null),
3895                                             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)
3896                                                                      ,
3897                                new EntityInfo("Customer", "customer", false, [
3898                                                    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),
3899                                                    new PropertyInfo("name", "name", new StringType(), 0, false, false, true, null, RelationType.None, null, null, null, null, null, null, null, null, null)], null)
3900                                                                      ];
3901 
3902 
3903 }
3904 
3905