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