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