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