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