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