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