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