1 /*
2  * Entity - Entity is an object-relational mapping tool for the D programming language. Referring to the design idea of JPA.
3  *
4  * Copyright (C) 2015-2018  Shanghai Putao Technology Co., Ltd
5  *
6  * Developer: HuntLabs.cn
7  *
8  * Licensed under the Apache-2.0 License.
9  *
10  */
11  
12 module hunt.entity.EntityInfo;
13 
14 import hunt.entity.eql.Common;
15 
16 import hunt.entity;
17 import hunt.entity.EntityDeserializer;
18 import hunt.entity.EntityMetaInfo;
19 import hunt.entity.DefaultEntityManagerFactory;
20 import hunt.entity.dialect;
21 import hunt.entity.EntityInfoMaker;
22 
23 import hunt.logging;
24 
25 import std.conv;
26 import std.string;
27 import std.traits;
28 import std.variant;
29 
30 class EntityInfo(T : Object, F : Object = T) {
31     
32     private EntityFieldInfo[string] _fields;
33     private string _factoryName = defaultEntityManagerFactoryName();
34     private string _tableName;
35     private string _tableNameInLower; // for PostgreSQL, the column's name will be converted to lowercase.
36     private string _entityClassName;
37     private string _autoIncrementKey;
38     private string _primaryKey;
39     private EntityManager _manager;
40     private Dialect _dialect;
41     private T _data;
42     private F _owner;
43     private string _tablePrefix;
44 
45     private EntityMetaInfo _metaInfo;
46 
47     // private string[string] _fieldColumnMaps;
48 
49     //auto mixin function
50     // public T deSerialize(Row row) {}
51     // public void setIncreaseKey(ref T entity, int value) {}
52     // public R getPrimaryValue() {}
53     // public void setPrimaryValue(ref T entity, int value) {}
54 
55     // pragma(msg, "T = "~T.stringof~ " F = "~F.stringof);
56     // pragma(msg,makeImport!(T)());
57     // pragma(msg,makeInitEntityData!(T,F)());
58     // pragma(msg,makeDeserializer!(T));
59     // pragma(msg,makeSetIncreaseKey!(T));
60     // pragma(msg,makeGetPrimaryValue!(T));
61     // pragma(msg,makeSetPrimaryValue!(T)());
62 
63 
64     mixin(makeImport!(T)());
65     mixin(makeInitEntityData!(T,F)());
66 
67     mixin(makeDeserializer!(T)());
68     mixin(makeSetIncreaseKey!(T)());
69     mixin(makeGetPrimaryValue!(T)());
70     mixin(makeSetPrimaryValue!(T)());
71 
72     this(EntityManager manager = null, T t = null, F owner = null)
73     {
74         version(HUNT_ENTITY_DEBUG) { 
75             warningf("T: %s, F: %s", T.stringof, F.stringof);
76         }
77 
78         if (t is null) {
79             _data = new T();
80         }
81         else {
82             _data = t;
83         }
84         
85         static if (is(T == F)){
86             _owner = _data;
87         }
88         else{
89             _owner = owner;
90         }
91         _manager = manager;
92         if (_manager) {
93             if(_data !is null)
94                 _data.setManager(_manager);
95             _tablePrefix = _manager.getPrefix();
96         }
97         
98         initializeEntityInfo();
99     }
100 
101     private void initializeEntityInfo() {
102         // _metaInfo = extractEntityInfo!(T)();
103         _metaInfo = T.metaInfo; // extractEntityInfo!(T)();
104 
105         _entityClassName = _metaInfo.simpleName;
106         _tableName = _tablePrefix ~ _metaInfo.tableName;
107         _tableNameInLower = _tableName.toLower();
108 
109         initEntityData();
110     }
111 
112     private string toColumnName(string fieldName) {
113         return _metaInfo.columnName(fieldName);
114     }
115 
116     public EntityFieldInfo getPrimaryField() {
117         if (_primaryKey.length > 0) 
118             return _fields[_primaryKey];
119         return null;
120     }
121 
122     public Variant[string] getInsertString() {
123         Variant[string] str;
124 
125         foreach(string fieldName, EntityFieldInfo info; _fields) {
126             string columnName = info.getColumnName();
127             Variant currentValue = info.getColumnFieldData();
128             
129             TypeInfo typeInfo = info.typeInfo();
130             if(typeInfo is null) {
131                 typeInfo = currentValue.type;
132             }
133 
134             version(HUNT_DB_DEBUG_MORE) {
135                 tracef("fieldName: %s, columnName: %s, type: %s, value: %s", 
136                     fieldName, columnName, typeInfo, currentValue.toString());
137             }
138             
139             // Skip the autoIncrementKey
140             if (columnName == _autoIncrementKey) 
141                 continue;
142             
143             // version(HUNT_DB_DEBUG) trace(currentValue.type);
144 
145             // skip Object member
146             if(typeid(typeInfo) == typeid(TypeInfo_Class) || 
147                 typeid(typeInfo) == typeid(TypeInfo_Struct) ) {
148                 version(HUNT_DB_DEBUG) warningf("Object member skipped: %s", fieldName);
149                 continue;
150             }
151 
152             if (columnName.empty()) {
153                 version(HUNT_DEBUG) warningf("The name of column for the field [%s] is empty.", fieldName);
154                 continue;
155             }
156 
157             if(!_manager.getDbOption().isPgsql()) {
158                 columnName = info.getFullColumn();
159             }
160 
161             if(columnName in str) {
162                 version(HUNT_DEBUG) {
163                     warningf("skip a existed column [%s] with value [%s].", columnName, currentValue.toString());
164                 }
165             } else {
166                 str[columnName] = currentValue;
167             }
168         }
169         return str;
170     }
171 
172     EntityFieldInfo opDispatch(string name)() 
173     {
174         version(HUNT_ENTITY_DEBUG) {
175             infof("getting field info for: %s", name);
176         }
177 
178         EntityFieldInfo fieldInfo = _fields.get(name,null);
179         if (fieldInfo is null) {
180             throw new EntityException("Cannot find entityfieldinfo by name : " ~ name);
181         } else {
182             version(HUNT_ENTITY_DEBUG_MORE) {
183                 tracef("The field info is %s", typeid(fieldInfo));
184             }
185         }
186         return fieldInfo;
187     }
188 
189     public string getFactoryName() { return _factoryName; }
190     public string getEntityClassName() { return _entityClassName; }
191     public string getTableName() { return _tableName; }
192     public string getAutoIncrementKey() { return _autoIncrementKey; }
193     public EntityFieldInfo[string] getFields() { return _fields; }
194     public string getPrimaryKeyString() { return _primaryKey; }
195     public EntityFieldInfo getSingleField(string name) { return _fields.get(name,null); }
196 
197     private string getCountAsName() {
198         if(_manager.getDbOption().isPgsql()) {
199             return EntityExpression.getCountAsName(_tableNameInLower);
200         } else {
201             return EntityExpression.getCountAsName(_tableName);
202         }
203     }
204 }
205 
206 string makeSetPrimaryValue(T)() {
207     string R;
208     string name;
209     foreach(memberName; __traits(derivedMembers, T)) {
210         static if (__traits(getProtection, __traits(getMember, T, memberName)) == "public") {
211             alias memType = typeof(__traits(getMember, T ,memberName));
212             static if (!isFunction!(memType) && hasUDA!(__traits(getMember, T ,memberName), PrimaryKey)) {
213                 R = typeof(__traits(getMember, T ,memberName)).stringof;
214                 name = memberName;
215             }
216         }
217     }
218     return `
219     public void setPrimaryValue(string value) {
220         _data.`~name~` = value.to!`~R~`;
221     }`;
222 }
223 
224 
225 string makeGetPrimaryValue(T)() {
226     string R;
227     string name;
228     foreach(memberName; __traits(derivedMembers, T)) {
229         static if (__traits(getProtection, __traits(getMember, T, memberName)) == "public") {
230             alias memType = typeof(__traits(getMember, T ,memberName));
231             static if (!isFunction!(memType) && hasUDA!(__traits(getMember, T ,memberName), PrimaryKey)) {
232                 R = typeof(__traits(getMember, T ,memberName)).stringof;
233                 name = memberName;
234             }
235         }
236     }
237     return `
238     public `~R~` getPrimaryValue() {
239         return _data.`~name~`;
240     }`;
241 }
242 
243 string makeSetIncreaseKey(T)() {
244     string name;
245     foreach(memberName; __traits(derivedMembers, T)) {
246         static if (__traits(getProtection, __traits(getMember, T, memberName)) == "public") {
247             alias memType = typeof(__traits(getMember, T ,memberName));
248             static if (!isFunction!(memType) && (hasUDA!(__traits(getMember, T ,memberName), AutoIncrement) || 
249                     hasUDA!(__traits(getMember, T ,memberName), Auto))) {
250                 name = memberName;
251             }
252         }
253     }
254     if (name == "")
255         return `
256     public void setIncreaseKey(ref T entity, int value) {
257     }`;
258     else
259         return `
260     public void setIncreaseKey(ref T entity, int value) {
261         entity.`~name~` = value;
262     }`;
263 }