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