public ForwardReference(IPersistEntity entity, ReferenceContext context, TableStore store) { Store = store; _handle = new XbimInstanceHandle(entity); Row = context.CurrentRow; Context = context; }
public ForwardReference(XbimInstanceHandle handle, ReferenceContext context, TableStore store) { Store = store; _handle = handle; Row = context.CurrentRow; Context = context; }
private void Join(IPersistEntity entity, ReferenceContext context, Stack <IPersistEntity> parents) { var temp = new Stack <IPersistEntity>(); IPersistEntity parent; while (parents.Count != 0) { parent = parents.Pop(); if (context.ContextType == ReferenceContextType.EntityList) { Store.AssignEntity(parent, entity, context); temp.Push(parent); break; } var e = Store.ResolveContext(context.ParentContext, -1, true); Store.AssignEntity(e, entity, context); context = context.ParentContext; entity = e; temp.Push(e); } //fill parents with the new stuff while (temp.Count != 0) { parent = temp.Pop(); parents.Push(parent); } }
internal void AssignEntity(IPersistEntity parent, IPersistEntity entity, ReferenceContext context) { if (context.MetaProperty != null && context.MetaProperty.IsDerived) { Log.WriteLine("It wasn't possible to add entity {0} as a {1} to parent {2} because it is a derived value", entity.ExpressType.ExpressName, context.Segment, parent.ExpressType.ExpressName); return; } var index = context.Index == null ? null : new[] { context.Index }; //inverse property if (context.MetaProperty != null && context.MetaProperty.IsInverse) { var remotePropName = context.MetaProperty.InverseAttributeProperty.RemoteProperty; var entityType = entity.ExpressType; var remoteProp = GetProperty(entityType, remotePropName); //it is enumerable inverse if (remoteProp.EnumerableType != null) { var list = remoteProp.PropertyInfo.GetValue(entity, index) as IList; if (list != null) { list.Add(parent); return; } } //it is a single inverse entity else { remoteProp.PropertyInfo.SetValue(entity, parent, index); return; } Log.WriteLine("It wasn't possible to add entity {0} as a {1} to parent {2}", entity.ExpressType.ExpressName, context.Segment, entityType.ExpressName); return; } //explicit property var info = context.PropertyInfo; if (context.ContextType == ReferenceContextType.EntityList) { var list = info.GetValue(parent, index) as IList; if (list != null) { list.Add(entity); return; } } else { if ((context.MetaProperty != null && context.MetaProperty.IsExplicit) || info.GetSetMethod() != null) { info.SetValue(parent, entity, index); return; } } Log.WriteLine("It wasn't possible to add entity {0} as a {1} to parent {2}", entity.ExpressType.ExpressName, context.Segment, parent.ExpressType.ExpressName); }
private IPersistEntity GetFirstValid(IEnumerable <object> entities, ReferenceContext context) { var enumerable = entities as IList <object> ?? entities.ToList(); //if there is only one in the list, retun that one if (enumerable.Count == 1) { return(enumerable.First() as IPersistEntity); } if (enumerable.Count == 0) { return(null); } //if context doesn't have any data at all on any level down if (!context.HasData) { return(null); } //this context has scalar data on its level if (context.ScalarChildren.Any(c => c.Values != null && c.Values.Length > 0)) { enumerable = enumerable.Where(e => TableStore.IsValidEntity(context, e)).ToList(); //if there is only one in the list, retun that one if (enumerable.Count == 1) { return(enumerable.First() as IPersistEntity); } if (enumerable.Count == 0) { return(null); } } return(enumerable.FirstOrDefault(e => context.EntityChildren.All(c => { var index = c.Index != null ? new[] { c.Index } : null; var value = c.PropertyInfo.GetValue(e, index); //this is equivalent of contains any valid if (c.ContextType == ReferenceContextType.EntityList) { var enu = value as IEnumerable <IPersistEntity>; if (enu == null) { return false; } return GetFirstValid(enu, c) != null; } var ent = value as IPersistEntity; return ent != null && TableStore.IsValidEntity(c, ent); })) as IPersistEntity); }
private ReferenceContext GetReferenceContext(ClassMapping mapping) { ReferenceContext context; if (_referenceContexts.TryGetValue(mapping, out context)) { return(context); } context = new ReferenceContext(this, mapping); _referenceContexts.Add(mapping, context); return(context); }
internal Type GetConcreteType(ReferenceContext context, ICell cell) { var cType = context.SegmentType; if (cType != null && !cType.Type.IsAbstract) { return(cType.Type); } //use custom type resolver if there is a one which can resolve this type if (cType != null && Resolvers != null && Resolvers.Any()) { var resolver = Resolvers.FirstOrDefault(r => r.CanResolve(cType)); if (resolver != null) { return(resolver.Resolve(cType.Type, cell, context.CMapping, context.Mapping)); } } if (context.PropertyInfo != null) { var pType = context.PropertyInfo.PropertyType; pType = GetNonNullableType(pType); if (pType.IsValueType || pType == typeof(string)) { return(pType); } if (typeof(IEnumerable).IsAssignableFrom(pType)) { pType = pType.GetGenericArguments()[0]; if (pType.IsValueType || pType == typeof(string)) { return(pType); } } if (Resolvers != null && Resolvers.Any()) { var resolver = Resolvers.FirstOrDefault(r => r.CanResolve(pType)); if (resolver != null) { return(resolver.Resolve(pType, cell, context.CMapping, context.Mapping)); } } } Log.WriteLine("It wasn't possible to find a non-abstract type for table {0}, class {1}", context.CMapping.TableName, context.CMapping.Class); return(null); }
/// <summary> /// Search the model for the entities satisfying the conditions in context /// </summary> /// <param name="context"></param> /// <returns></returns> internal IEnumerable <IPersistEntity> GetReferencedEntities(ReferenceContext context) { var type = context.SegmentType; //return empty enumeration in case there are identifiers but no data if (context.TypeHintMapping == null && context.TableHintMapping == null && context.ScalarChildren.Any() && !context.HasData) { return(Enumerable.Empty <IPersistEntity>()); } //we don't have any data so use just a type for the search return(!context.ScalarChildren.Any() ? Model.Instances.OfType(type.Name, true) : Model.Instances.OfType(type.Name, true).Where(e => IsValidEntity(context, e))); }
private static bool IsValidInContext(ReferenceContext scalar, object entity) { var prop = scalar.PropertyInfo; var vals = scalar.Values; var eVal = prop.GetValue(entity, null); if (scalar.ContextType != ReferenceContextType.ScalarList) { return(eVal != null && vals.Any(v => v != null && v.Equals(eVal))); } var list = eVal as IEnumerable; return(list != null && //it might be a multivalue list.Cast <object>().All(item => vals.Any(v => v.Equals(item)))); }
internal static bool IsValidEntity(ReferenceContext context, object entity) { if (context.ScalarChildren.Count == 0) { return(true); } //if it might have identifiers but doesn't have a one it can't find any if (!context.HasData) { return(false); } return(context.ScalarChildren .Where(s => s.Values != null && s.Values.Length > 0) .All(scalar => IsValidInContext(scalar, entity))); }
private void AddMapping(PropertyMapping pMapping, List <string> segments) { if (segments == null || !segments.Any()) { //this is a leaf Mapping = pMapping; return; } var segment = segments.First(); var segmentName = segment.Split('\\')[0]; //handle special cases - type hints switch (segment) { case "[table]": TableHintMapping = pMapping; return; case "[type]": TypeHintMapping = pMapping; return; } var existChild = Children.FirstOrDefault(c => c.Segment == segmentName); if (existChild != null) { existChild.AddMapping(pMapping, segments.GetRange(1, segments.Count - 1)); } else { var child = new ReferenceContext(segment, this, Store, CMapping); child.AddMapping(pMapping, segments.GetRange(1, segments.Count - 1)); Children.Add(child); //cache scalar children for optimization if (child.ContextType == ReferenceContextType.Scalar || child.ContextType == ReferenceContextType.ScalarList) { _scalarChildren.Add(child); } } }
private IPersistEntity LoadFromRow(IRow row, ReferenceContext context, IRow lastRow, IPersistEntity lastEntity) { //load data into the context context.LoadData(row, true); var multirow = IsMultiRow(row, context.CMapping, lastRow); if (multirow) { //only add multivalue to the multivalue properties of last entity var subContexts = context.AllScalarChildren .Where(c => c.Mapping.MultiRow != MultiRow.None) .Select(c => { //get to the first list level up or on the base level if it is a scalar list if (c.ContextType == ReferenceContextType.ScalarList) { return(c); } while (c != null && c.ContextType != ReferenceContextType.EntityList) { c = c.ParentContext; } return(c); }) .Where(c => c != null) .Distinct(); foreach (var ctx in subContexts) { ResolveMultiContext(ctx, lastEntity); } return(lastEntity); } //get type of the coresponding object from ClassMapping or from a type hint, create instance return(ResolveContext(context, -1, false)); }
private ExpressType GetConcreteType(ReferenceContext context) { var cType = context.SegmentType; if (cType != null && !cType.Type.IsAbstract) { return(cType); } //use fallback to retrieve a non-abstract type (defined in a configuration file?) var fbTypeName = context.CMapping.FallBackConcreteType; if (context.IsRoot && !string.IsNullOrWhiteSpace(fbTypeName)) { var eType = MetaData.ExpressType(fbTypeName.ToUpper()); if (eType != null && !eType.Type.IsAbstract) { return(eType); } } //use custom type resolver if there is a one which can resolve this type if (cType != null && Resolvers != null && Resolvers.Any()) { var resolver = Resolvers.FirstOrDefault(r => r.CanResolve(cType)); if (resolver != null) { return(resolver.Resolve(cType, context, MetaData)); } } Log.WriteLine("It wasn't possible to find a non-abstract type for table {0}, class {1}", context.CMapping.TableName, context.CMapping.Class); return(null); }
/// <summary> /// Returns true if it exists, FALSE if new entity fas created and needs to be filled in with data /// </summary> /// <param name="context"></param> /// <param name="entity"></param> /// <param name="type"></param> /// <param name="scalarIndex">Index to field of values to be used to create the key. If -1 no index is used and all values are used.</param> /// <returns></returns> private bool GetOrCreateGlobalEntity(ReferenceContext context, out IPersistEntity entity, ExpressType type, int scalarIndex) { type = type ?? GetConcreteType(context); Dictionary <string, IPersistEntity> entities; if (!_globalEntities.TryGetValue(type, out entities)) { entities = new Dictionary <string, IPersistEntity>(); _globalEntities.Add(type, entities); } var keys = scalarIndex > -1 ? context.AllScalarChildren.OrderBy(c => c.Segment) .Where(c => c.Values != null) .Select(c => { if (c.Values.Length == 1) { return(c.Values[0]); } return(c.Values.Length >= scalarIndex + 1 ? c.Values[scalarIndex] : null); }).Where(v => v != null): context.AllScalarChildren.OrderBy(c => c.Segment) .Where(c => c.Values != null) .SelectMany(c => c.Values.Where(cv => cv != null).Select(v => v.ToString())); var key = string.Join(", ", keys); if (entities.TryGetValue(key, out entity)) { return(true); } entity = Model.Instances.New(type.Type); entities.Add(key, entity); return(false); }
private void AddToPath(ReferenceContext targetContext, IPersistEntity parent, IPersistEntity child) { //get context path from root entity var ctxStack = new Stack <ReferenceContext>(); var entityStack = new Stack <IPersistEntity>(); var context = targetContext; while (!context.IsRoot && context.ContextType != ReferenceContextType.Parent) { ctxStack.Push(context); context = context.ParentContext; } var entity = parent; while (ctxStack.Count != 0) { context = ctxStack.Pop(); entityStack.Push(entity); //browse to the level of the bottom context and call ResolveContext there var index = context.Index != null ? new[] { context.Index } : null; var value = context.PropertyInfo.GetValue(entity, index); if (context.ContextType == ReferenceContextType.Entity) { var e = value as IPersistEntity; //if it is null, create a new one or assign the child if (e == null) { e = context == targetContext ? child : Store.ResolveContext(context, -1, true); Store.AssignEntity(entity, e, context); entity = e; continue; } //verify that this is the desired one by the values. If not, create a new one on this level and higher if (TableStore.IsValidEntity(context, e)) { entity = e; continue; } //create a new one and assign it higher e = context == targetContext ? child : Store.ResolveContext(context, -1, true); Join(e, context, entityStack); entity = e; continue; } //it should be enumerable var entities = value as IEnumerable; if (entities == null) { Store.Log.WriteLine("It wasn't possible to browse to the data entry point."); return; } if (context == targetContext) { Store.AssignEntity(entity, child, context); return; } //get first valid entity entity = GetFirstValid(entities.Cast <object>(), context); //entity = entities.Cast<object>().FirstOrDefault(e => TableStore.IsValidEntity(context, e)) as IPersistEntity; if (entity != null) { continue; } //create new entity and assign it on a higher level entity = Store.ResolveContext(context, -1, true); AddToPath(context, parent, entity); } }
private ReferenceContext(string segment, ReferenceContext parent, TableStore store, ClassMapping cMapping) { Store = store; CMapping = cMapping; //init lists Children = new List <ReferenceContext>(); ParentContext = parent; //try to extract TypeOf part of the path var parts = segment.Split('\\'); segment = parts[0]; var typeHint = parts.Length > 1 ? parts[1] : null; Segment = segment; //set up path type hint if it is defined if (!string.IsNullOrWhiteSpace(typeHint)) { PathTypeHint = Store.MetaData.ExpressType(typeHint.ToUpper()); } if (segment == "parent") { PathTypeHint = Store.MetaData.ExpressType(CMapping.ParentClass.ToUpper()); ContextType = ReferenceContextType.Parent; return; } Index = TableStore.GetPropertyIndex(ref segment); PropertyInfo = Store.GetPropertyInfo(segment, parent.SegmentType, Index); MetaProperty = Store.GetProperty(parent.SegmentType, segment); var info = PropertyInfo != null ? PropertyInfo.PropertyType : (MetaProperty != null ? MetaProperty.EnumerableType ?? MetaProperty.PropertyInfo.PropertyType : null); if (info == null) { Store.Log.WriteLine("Type {0} doesn't have a property {1}.", parent.PathTypeHint.ExpressName, segment); return; } PropertyTypeHint = Store.MetaData.ExpressType(MetaProperty != null ? MetaProperty.EnumerableType ?? MetaProperty.PropertyInfo.PropertyType : (PropertyInfo != null ? PropertyInfo.PropertyType : null) ); //set up type of the context var isEnumerable = MetaProperty != null && MetaProperty.EnumerableType != null; if (isEnumerable) { if (MetaProperty.EnumerableType.IsValueType || MetaProperty.EnumerableType == typeof(string) || typeof(IExpressValueType).IsAssignableFrom(MetaProperty.EnumerableType)) { ContextType = ReferenceContextType.ScalarList; } else { ContextType = ReferenceContextType.EntityList; } } else { if (info.IsValueType || info == typeof(string) || typeof(IExpressValueType).IsAssignableFrom(info)) { ContextType = ReferenceContextType.Scalar; } else { ContextType = ReferenceContextType.Entity; } } }
/// <summary> /// /// </summary> /// <param name="context">Reference context of the data</param> /// <param name="scalarIndex">Index of value to be used in a value list in case of multi values</param> /// <param name="onlyScalar"></param> /// <returns></returns> internal IPersistEntity ResolveContext(ReferenceContext context, int scalarIndex, bool onlyScalar) { IPersistEntity entity = null; var eType = GetConcreteType(context); if (IsGlobalType(eType.Type)) { //it is a global type but there are no values to fill in if (!context.AllScalarChildren.Any(c => c.Values != null && c.Values.Length > 0)) { return(null); } //it is a global entity and it was filled in with the data before if (GetOrCreateGlobalEntity(context, out entity, eType, scalarIndex)) { return(entity); } } //create new entity if new global one was not created if (entity == null) { entity = Model.Instances.New(eType.Type); } //scalar values to be set to the entity foreach (var scalar in context.ScalarChildren) { var values = scalar.Values; if (values == null || values.Length == 0) { continue; } if (scalar.ContextType == ReferenceContextType.ScalarList) { //is should be ItemSet which is always initialized and inherits from IList var list = scalar.PropertyInfo.GetValue(entity, null) as IList; if (list == null) { continue; } foreach (var value in values) { list.Add(value); } continue; } //it is a single value var val = scalarIndex < 0 ? values[0]: (values.Length >= scalarIndex + 1 ? values[scalarIndex] : null); if (val != null) { scalar.PropertyInfo.SetValue(entity, val, scalar.Index != null ? new[] { scalar.Index } : null); } } if (onlyScalar) { return(entity); } //nested entities (global, local, referenced) foreach (var childContext in context.EntityChildren) { if (childContext.IsReference) { _forwardReferences.Enqueue(new ForwardReference(entity, childContext, this)); continue; } if (childContext.ContextType == ReferenceContextType.EntityList) { var depth = childContext.ScalarChildren.Where(c => c.Values != null) .Select(c => c.Values.Length) .OrderByDescending(v => v) .FirstOrDefault(); for (var i = 0; i < depth; i++) { var child = depth == 1 ? ResolveContext(childContext, -1, false) : ResolveContext(childContext, i, false); AssignEntity(entity, child, childContext); } continue; } //it is a single entity var cEntity = ResolveContext(childContext, -1, false); AssignEntity(entity, cEntity, childContext); } var parentContext = context.Children.FirstOrDefault(c => c.ContextType == ReferenceContextType.Parent); if (parentContext != null) { _forwardReferences.Enqueue(new ForwardReference(entity, parentContext, this)); } return(entity); }
/// <summary> /// This is used for a multi-row instances where only partial context needs to be processed /// </summary> /// <param name="subContext"></param> /// <param name="rootEntity"></param> private void ResolveMultiContext(ReferenceContext subContext, IPersistEntity rootEntity) { //get context path from root entity var ctxStack = new Stack <ReferenceContext>(); var context = subContext; while (context != null) { ctxStack.Push(context); context = context.ParentContext; } //use path to get to the bottom of the stact and add the value to it context = ctxStack.Pop(); var entity = rootEntity; //stop one level above the original subcontext while (ctxStack.Peek() != subContext) { //browse to the level of the bottom context and call ResolveContext there var index = context.Index != null ? new[] { context.Index } : null; var value = context.PropertyInfo.GetValue(rootEntity, index); if (value == null) { Log.WriteLine("It wasn't possible to browse to the data entry point."); return; } if (context.ContextType == ReferenceContextType.Entity) { entity = value as IPersistEntity; continue; } var entities = value as IEnumerable; if (entities == null) { Log.WriteLine("It wasn't possible to browse to the data entry point."); return; } foreach (var e in entities) { if (!IsValidEntity(context, e)) { continue; } entity = e as IPersistEntity; break; } } if (subContext.IsReference) { var reference = new ForwardReference(entity, subContext, this); _forwardReferences.Enqueue(reference); return; } if (subContext.ContextType == ReferenceContextType.EntityList) { var child = ResolveContext(subContext, -1, false); AssignEntity(entity, child, subContext); return; } if (subContext.ContextType == ReferenceContextType.ScalarList) { var list = subContext.PropertyInfo.GetValue(entity, null) as IList; if (list != null && subContext.Values != null && subContext.Values.Length > 0) { list.Add(subContext.Values[0]); } } }