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