PageRenderTime 42ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 0ms

/source/library/Interlace.UserInterface/Utilities/EntityClonerBase.cs

https://bitbucket.org/VahidN/interlace
C# | 357 lines | 263 code | 63 blank | 31 comment | 29 complexity | 7b999501d74a7e49501d79d32143727b MD5 | raw file
  1. #region Using Directives and Copyright Notice
  2. // Copyright (c) 2007-2010, Computer Consultancy Pty Ltd
  3. // All rights reserved.
  4. //
  5. // Redistribution and use in source and binary forms, with or without
  6. // modification, are permitted provided that the following conditions are met:
  7. // * Redistributions of source code must retain the above copyright
  8. // notice, this list of conditions and the following disclaimer.
  9. // * Redistributions in binary form must reproduce the above copyright
  10. // notice, this list of conditions and the following disclaimer in the
  11. // documentation and/or other materials provided with the distribution.
  12. // * Neither the name of the Computer Consultancy Pty Ltd nor the
  13. // names of its contributors may be used to endorse or promote products
  14. // derived from this software without specific prior written permission.
  15. //
  16. // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  17. // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  18. // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  19. // ARE DISCLAIMED. IN NO EVENT SHALL COMPUTER CONSULTANCY PTY LTD BE LIABLE
  20. // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  21. // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  22. // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  23. // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  24. // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  25. // OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
  26. // DAMAGE.
  27. using System;
  28. using System.Collections.Generic;
  29. using System.ComponentModel;
  30. using System.Text;
  31. using SD.LLBLGen.Pro.ORMSupportClasses;
  32. using Interlace.Collections;
  33. #endregion
  34. namespace Interlace.Utilities
  35. {
  36. public abstract class EntityClonerBase
  37. {
  38. Type _entityType;
  39. EntityBase2 _entityPrototype;
  40. EntityClonerFieldList _fieldsToCopy;
  41. EntityClonerFieldList _fieldsToIgnore;
  42. EntityClonerRelationList _relationsToCopy;
  43. EntityClonerRelationList _relationsToIgnore;
  44. bool _isRecursive;
  45. public EntityClonerBase(Type entityType, EntityBase2 entityPrototype)
  46. {
  47. _entityType = entityType;
  48. _entityPrototype = entityPrototype;
  49. _fieldsToCopy = new EntityClonerFieldList();
  50. _fieldsToIgnore = new EntityClonerFieldList();
  51. _relationsToCopy = new EntityClonerRelationList(true);
  52. _relationsToIgnore = new EntityClonerRelationList(false);
  53. _isRecursive = true;
  54. }
  55. protected abstract EntityClonerMapBase CreateMap();
  56. public bool IsRecursive
  57. {
  58. get { return _isRecursive; }
  59. set { _isRecursive = value; }
  60. }
  61. void EnsureFieldIsNotPrimaryKey(EntityClonerField field)
  62. {
  63. if (field.IsPrimaryKey)
  64. {
  65. throw new EntityClonerException(string.Format(
  66. "Primary keys should never be cloned; the attempt to copy the primary key " +
  67. "field \"{0}\" in \"{1}\" failed.",
  68. field.Name, _entityType.Name));
  69. }
  70. }
  71. public void EnsureAll()
  72. {
  73. EnsureFieldSetsAreDisjoint();
  74. EnsureAllFieldsAreNotPrimaryKeys();
  75. EnsureAllFieldsMentioned();
  76. if (_isRecursive)
  77. {
  78. EnsureAllCollectionsMentioned();
  79. EnsureRelationSetsAreDisjoint();
  80. }
  81. else
  82. {
  83. if (_relationsToCopy.Dictionary.Count > 0 || _relationsToIgnore.Dictionary.Count > 0)
  84. {
  85. throw new EntityClonerException(string.Format(
  86. "An entity cloner for a \"{0}\" is marked as non-recursive but relations " +
  87. "have been added to either the copy or ignore lists.", _entityType.Name));
  88. }
  89. }
  90. }
  91. public bool UpdateBase(EntityBase2 sourceEntity, EntityBase2 destinationEntity)
  92. {
  93. EnsureAll();
  94. if (_isRecursive)
  95. {
  96. throw new EntityClonerException(string.Format(
  97. "An update was attempted on an entity cloner for a \"{0}\", but the cloner is marked " +
  98. "as recursive and is therefore not suitable for updates.", _entityType.Name));
  99. }
  100. bool updatesRequired = false;
  101. foreach (EntityClonerField field in _fieldsToCopy.Set)
  102. {
  103. if (!field.IsPrimaryKey)
  104. {
  105. object sourceValue = sourceEntity.Fields[field.FieldIndex].CurrentValue;
  106. if (!object.Equals(destinationEntity.Fields[field.FieldIndex].CurrentValue, sourceValue))
  107. {
  108. destinationEntity.SetNewFieldValue(field.FieldIndex, sourceValue);
  109. updatesRequired = true;
  110. }
  111. }
  112. }
  113. return updatesRequired;
  114. }
  115. public EntityClonerMapBase CloneBase(EntityBase2 sourceEntity)
  116. {
  117. EnsureAll();
  118. EntityClonerMapBase map = CreateMap();
  119. CloneBaseRecursive(sourceEntity, map);
  120. map.SetRootEntities(sourceEntity, map.Mappings[sourceEntity]);
  121. return map;
  122. }
  123. public EntityBase2 CloneBaseRecursive(EntityBase2 sourceEntity, EntityClonerMapBase map)
  124. {
  125. // Return an existing entity if it has already been cloned; when the graph passed in is a DAG but not
  126. // a tree this will occur:
  127. if (map.Mappings.ContainsKey(sourceEntity))
  128. {
  129. return map.Mappings[sourceEntity];
  130. }
  131. EntityBase2 destinationEntity = sourceEntity.GetEntityFactory().Create() as EntityBase2;
  132. foreach (EntityClonerField field in _fieldsToCopy.Set)
  133. {
  134. if (!field.IsPrimaryKey)
  135. {
  136. destinationEntity.Fields[field.FieldIndex].CurrentValue =
  137. sourceEntity.Fields[field.FieldIndex].CurrentValue;
  138. }
  139. }
  140. map.AddMappingOfEntities(sourceEntity, destinationEntity);
  141. if (_isRecursive)
  142. {
  143. foreach (IPrefetchPathElement2 pathElement in _relationsToCopy.Dictionary.Keys)
  144. {
  145. object relatedDataObject = sourceEntity.GetRelatedData()[pathElement.PropertyName];
  146. if (relatedDataObject is IEntityCollection2)
  147. {
  148. IEntityCollection2 collection = relatedDataObject as IEntityCollection2;
  149. if (collection != null)
  150. {
  151. foreach (EntityBase2 entity in collection)
  152. {
  153. EntityBase2 newEntity = _relationsToCopy.Dictionary[pathElement].CloneBaseRecursive(entity, map);
  154. destinationEntity.SetRelatedEntityProperty(pathElement.PropertyName, newEntity);
  155. }
  156. }
  157. }
  158. else if (relatedDataObject is EntityBase2)
  159. {
  160. EntityBase2 relatedEntity = relatedDataObject as EntityBase2;
  161. EntityBase2 newEntity = _relationsToCopy.Dictionary[pathElement].CloneBaseRecursive(relatedEntity, map);
  162. destinationEntity.SetRelatedEntityProperty(pathElement.PropertyName, newEntity);
  163. }
  164. }
  165. }
  166. return destinationEntity;
  167. }
  168. private void EnsureRelationSetsAreDisjoint()
  169. {
  170. // Not yet implemented.
  171. }
  172. private void EnsureFieldSetsAreDisjoint()
  173. {
  174. Set<EntityClonerField> intersection =
  175. Set<EntityClonerField>.Intersection(_fieldsToCopy.Set, _fieldsToIgnore.Set);
  176. if (intersection.Count > 0)
  177. {
  178. throw new EntityClonerException(string.Format(
  179. "One or more of the fields (\"{0}\") are both in the copy and ignore lists for " +
  180. "an entity cloner for a \"{1}\".",
  181. NaturalStrings.FormatList(intersection, "\"{0}\""), _entityType.Name));
  182. }
  183. }
  184. void EnsureAllCollectionsMentioned()
  185. {
  186. List<IEntityRelation> missingRelations = new List<IEntityRelation>();
  187. foreach (IEntityRelation relation in _entityPrototype.GetAllRelations())
  188. {
  189. if (IsRelationMissingFrom(relation, _relationsToCopy) && IsRelationMissingFrom(relation, _relationsToIgnore))
  190. {
  191. missingRelations.Add(relation);
  192. }
  193. }
  194. if (missingRelations.Count > 0)
  195. {
  196. List<string> relationNames = missingRelations.ConvertAll<string>(
  197. delegate(IEntityRelation relation) { return relation.MappedFieldName; });
  198. throw new EntityClonerException(string.Format(
  199. "The {0} \"{1}\" was not mentioned while attempting to clone a \"{2}\". " +
  200. "All fields and relations must be marked copied or ignored before the cloner can be used.",
  201. relationNames.Count == 1 ? "relation" : "relations",
  202. NaturalStrings.FormatList(relationNames, "\"{0}\""),
  203. _entityType.Name));
  204. }
  205. }
  206. bool IsRelationMissingFrom(IEntityRelation relation, EntityClonerRelationList list)
  207. {
  208. foreach (IPrefetchPathElement2 pathElement in list.Dictionary.Keys)
  209. {
  210. if (pathElement.Relation.MappedFieldName == relation.MappedFieldName)
  211. {
  212. return false;
  213. }
  214. }
  215. return true;
  216. }
  217. void EnsureAllFieldsAreNotPrimaryKeys()
  218. {
  219. foreach (EntityClonerField field in _fieldsToCopy.Set)
  220. {
  221. EnsureFieldIsNotPrimaryKey(field);
  222. }
  223. foreach (EntityClonerField field in _fieldsToIgnore.Set)
  224. {
  225. EnsureFieldIsNotPrimaryKey(field);
  226. }
  227. }
  228. void EnsureAllFieldsMentioned()
  229. {
  230. // Find the set of fields in the entity:
  231. Set<EntityClonerField> prototypeFields = new Set<EntityClonerField>();
  232. foreach (EntityField2 field in _entityPrototype.Fields)
  233. {
  234. if (!field.IsPrimaryKey)
  235. {
  236. prototypeFields.UnionUpdate(new EntityClonerField(field));
  237. }
  238. }
  239. // Find all mentioned fields:
  240. Set<EntityClonerField> mentionedFields = Set<EntityClonerField>.Union(
  241. _fieldsToCopy.Set, _fieldsToIgnore.Set);
  242. // Check the difference:
  243. Set<EntityClonerField> missingFields = Set<EntityClonerField>.Difference(
  244. prototypeFields, mentionedFields);
  245. if (missingFields.Count > 0)
  246. {
  247. throw new EntityClonerException(string.Format(
  248. "The {0} \"{1}\" was not mentioned while attempting to clone a \"{2}\". " +
  249. "All fields and relations must be marked copied or ignored before the cloner can be used.",
  250. missingFields.Count == 1 ? "entity field" : "entity fields",
  251. NaturalStrings.FormatList(missingFields, "\"{0}\""),
  252. _entityType.Name));
  253. }
  254. Set<EntityClonerField> extraFields = Set<EntityClonerField>.Difference(
  255. mentionedFields, prototypeFields);
  256. if (extraFields.Count > 0)
  257. {
  258. throw new EntityClonerException(string.Format(
  259. "The {0} \"{1}\" was included in a cloner for a \"{2}\" when the field is not a field " +
  260. "in this entity.",
  261. extraFields.Count == 1 ? "entity field" : "entity fields",
  262. NaturalStrings.FormatList(extraFields, "\"{0}\""),
  263. _entityType.Name));
  264. }
  265. }
  266. bool IsFieldMissingFrom(EntityClonerFieldList fields, EntityField2 comparisonField)
  267. {
  268. foreach (EntityClonerField field in fields.Set)
  269. {
  270. if (field.Name == comparisonField.Name)
  271. {
  272. return false;
  273. }
  274. }
  275. return true;
  276. }
  277. public EntityClonerFieldList Copied
  278. {
  279. get { return _fieldsToCopy; }
  280. }
  281. public EntityClonerFieldList Ignored
  282. {
  283. get { return _fieldsToIgnore; }
  284. }
  285. public EntityClonerRelationList CopiedRelations
  286. {
  287. get { return _relationsToCopy; }
  288. }
  289. public EntityClonerRelationList IgnoredRelations
  290. {
  291. get { return _relationsToIgnore; }
  292. }
  293. }
  294. }