/// <summary> /// /// </summary> /// <param name="attributes">The list of available attributes</param> /// <param name="container">This is the type of container we are searchig attributes for.</param> /// <returns>The attributes matching the <see cref="EdiStructureType"/></returns> public static IEnumerable<EdiAttribute> OfType(this IEnumerable<EdiAttribute> attributes, EdiStructureType container) { var typesToSearch = new Type[0]; switch (container) { case EdiStructureType.None: case EdiStructureType.Interchange: break; case EdiStructureType.Group: typesToSearch = new [] { typeof(EdiGroupAttribute) }; break; case EdiStructureType.Message: typesToSearch = new [] { typeof(EdiMessageAttribute) }; break; case EdiStructureType.SegmentGroup: typesToSearch = new [] { typeof(EdiSegmentGroupAttribute) }; break; case EdiStructureType.Segment: typesToSearch = new [] { typeof(EdiSegmentAttribute), typeof(EdiSegmentGroupAttribute) }; break; case EdiStructureType.Element: typesToSearch = new [] { typeof(EdiElementAttribute) }; break; default: break; } return null == typesToSearch ? Enumerable.Empty<EdiAttribute>() : attributes.Where(a => typesToSearch.Contains(a.GetType())); }
public EdiStructure(EdiStructureType container, object instance, int index, Queue<EdiEntry> cache) { ValidationUtils.ArgumentNotNull(instance, "instance"); _Container = container; _Instance = instance; _Index = index; _Descriptor = typeStore.Get(instance.GetType()); _CachedReads = cache; }
/// <summary> /// Initializes a new instance of the <see cref="T:indice.Edi.Serialization.EdiStructure"/> class. /// </summary> /// <param name="container">Container.</param> /// <param name="instance">Instance.</param> /// <param name="index">Index.</param> /// <param name="cache">Cache.</param> public EdiStructure(EdiStructureType container, object instance, int index, Queue <EdiEntry> cache) { ValidationUtils.ArgumentNotNull(instance, "instance"); _Container = container; _Instance = instance; _Index = index; _Descriptor = typeStore.Get(instance.GetType()); _CachedReads = cache; }
public EdiStructure(EdiStructureType structureType, EdiStructure parent, EdiPropertyDescriptor property, object instance) : this(structureType, parent, property, instance, (structureType == EdiStructureType.Element && property.PathInfo.PathInternal.Element.HasIndex) ? property.PathInfo.PathInternal.Element.Index : 0, new Queue <EdiEntry>()) { }
public EdiStructure(EdiStructureType structureType, EdiStructure parent, EdiPropertyDescriptor property, object instance, int index, Queue <EdiEntry> cache) { ValidationUtils.ArgumentNotNull(instance, "instance"); _StructureType = structureType; _Container = parent; _Instance = instance; _Index = index; _Descriptor = typeStore.Get(instance.GetType()); _CachedReads = cache; _Conditions = Descriptor.Attributes.OfType <EdiConditionAttribute>().Concat(property?.Conditions ?? new EdiConditionAttribute[0]).ToArray(); _ConditionStackMode = _Conditions.Length > 0 && ( property?.ConditionStackMode == EdiConditionStackMode.Any || Descriptor.Attributes.OfType <EdiAnyAttribute>().Any()) ? EdiConditionStackMode.Any : EdiConditionStackMode.All; }
internal bool TryCreateContainer(EdiReader reader, Stack <EdiStructure> stack, EdiStructureType newContainer) { var index = 0; if (stack.Count == 0) { return(false); } // clear once upon segment start. This is done here in order to keep any findings for future use if (newContainer == EdiStructureType.SegmentGroup || newContainer == EdiStructureType.Segment) { stack.Peek().CachedReads.Clear(); } if (newContainer == EdiStructureType.SegmentGroup && stack.Peek().StructureType >= EdiStructureType.SegmentGroup) { // strict hierarchy while (stack.Peek().StructureType > newContainer) { var previous = stack.Pop(); // close this level } // nested hierarchy var readerSegment = reader.Value; foreach (var level in stack) { if (!level.IsGroup) { continue; } var groupStart = level.GroupStart; var sequenceEnd = level.SequenceEnd; if (groupStart.Segment.Equals(readerSegment)) { if (PositionMatchesStructure(reader, level, readerSegment as string) || // if new occurance of my level or sibling found FindForCurrentSegment(reader, level, EdiStructureType.SegmentGroup) == null) // if cannot advance either. { level.Close(); // Close this level index = level.Index + 1; continue; } } else if (sequenceEnd.HasValue && sequenceEnd.Value.Segment.Equals(readerSegment)) { level.Close(); // Close this level continue; } else if (level.GroupMembers.Length > 1 && !level.GroupContains(readerSegment as string)) { level.Close(); // Close this level continue; } } var clearUpTo = stack.Reverse().FirstOrDefault(x => x.IsClosed)?.Container; if (clearUpTo != null) { while (stack.Peek() != clearUpTo) { stack.Pop(); } } } else { // strict hierarchy while (stack.Peek().StructureType >= newContainer) { var previous = stack.Pop(); // close this level if (previous.StructureType == newContainer) { index = previous.Index + 1; // seed collection index } } } var current = stack.Peek(); var property = default(EdiPropertyDescriptor); var childCache = default(Queue <EdiEntry>); switch (newContainer) { case EdiStructureType.SegmentGroup: property = FindForCurrentSegment(reader, current, newContainer); break; case EdiStructureType.Segment: property = FindForCurrentSegment(reader, current, newContainer); break; case EdiStructureType.Element: property = FindForCurrentElement(reader, current, newContainer, out childCache); break; default: property = FindForCurrentLogicalStructure(reader, current, newContainer); break; } if (property == null) { return(false); } var propValue = property.Info.GetValue(current.Instance, null); if (propValue == null) { if (property.Info.PropertyType.IsCollectionType()) { if (property.Info.PropertyType.IsArray) { var initalLength = 0; propValue = Activator.CreateInstance(property.Info.PropertyType, initalLength); } else { var baseType = typeof(List <>); var genericType = baseType.MakeGenericType(property.Info.PropertyType.GetGenericArguments().First()); propValue = Activator.CreateInstance(genericType); } } else { propValue = Activator.CreateInstance(property.Info.PropertyType); } property.Info.SetValue(current.Instance, propValue); } if (propValue is IList) { var itemType = default(Type); var item = default(object); if (property.Info.PropertyType.IsArray) { itemType = property.Info.PropertyType.GetElementType(); item = Activator.CreateInstance(itemType); var newArray = Array.CreateInstance(itemType, ((IList)propValue).Count + 1); var oldArray = ((Array)propValue); Array.Copy(oldArray, newArray, oldArray.Length); newArray.SetValue(item, newArray.Length - 1); property.Info.SetValue(current.Instance, newArray); } else { itemType = property.Info.PropertyType.GetGenericArguments().First(); item = Activator.CreateInstance(itemType); ((IList)propValue).Add(item); } propValue = item; } stack.Push(new EdiStructure(newContainer, current, property, propValue, index, childCache ?? current.CachedReads)); return(true); }
/// <summary> /// Finds for current element. /// </summary> /// <returns>The for current element.</returns> /// <param name="reader">Reader.</param> /// <param name="currentStructure">Current structure.</param> /// <param name="newContainerType">New container type.</param> private EdiPropertyDescriptor FindForCurrentElement(EdiReader reader, EdiStructure currentStructure, EdiStructureType newContainerType) { var candidates = currentStructure.GetMatchingProperties(newContainerType); if (candidates.Length == 0) { return(null); } var property = default(EdiPropertyDescriptor); if (reader.TokenType == EdiToken.ElementStart) { var matches = candidates.Where(p => p.PathInfo.PathInternal.ToString("E").Equals(reader.Path)).ToArray(); if (matches.Length == 0) { property = null; } else if (matches.Length == 1 && matches[0].ConditionInfo == null) { property = matches[0]; } else { property = ConditionalMatch(reader, currentStructure, newContainerType, matches); } } return(property); }
/// <summary> /// Conditionals the match. /// </summary> /// <returns>The match.</returns> /// <param name="reader">Reader.</param> /// <param name="currentStructure">Current structure.</param> /// <param name="newContainerType">New container type.</param> /// <param name="matches">Matches.</param> private static EdiPropertyDescriptor ConditionalMatch(EdiReader reader, EdiStructure currentStructure, EdiStructureType newContainerType, EdiPropertyDescriptor[] matches) { if (!matches.All(p => p.ConditionInfo != null)) { throw new EdiException( "More than one properties on type '{0}' have the '{1}' attribute. Please add a 'Condition' attribute to all properties in order to discriminate where each {2} will go." .FormatWith(CultureInfo.InvariantCulture, currentStructure.Descriptor.ClrType.Name, newContainerType, newContainerType)); } if (matches.Select(p => p.Path).Distinct().Count() != 1) { throw new EdiException("More than one properties on type '{0}' have the '{1}' attribute but the 'Condition' attribute has a different search path declared." .FormatWith(CultureInfo.InvariantCulture, currentStructure.Descriptor.ClrType.Name, newContainerType)); } var readCache = currentStructure.CachedReads; var path = string.Empty; do { reader.Read(); path = reader.Path; readCache.Enqueue(new EdiEntry(path, reader.TokenType, reader.Value as string)); } while (reader.TokenType != EdiToken.SegmentStart && matches[0].Path != path); var discriminator = reader.ReadAsString(); var property = matches.SingleOrDefault(p => p.ConditionInfo.MatchValue == discriminator); readCache.Enqueue(new EdiEntry(path, reader.TokenType, discriminator)); return(property); }
private static EdiPropertyDescriptor ConditionalMatch(EdiReader reader, EdiStructure currentStructure, EdiStructureType newContainerType, EdiPropertyDescriptor[] candidates) { if (!candidates.All(p => p.Conditions != null)) { throw new EdiException( "More than one properties on type '{0}' have the '{1}' attribute. Please add a 'Condition' attribute to all properties in order to discriminate where each {2} will go." .FormatWith(CultureInfo.InvariantCulture, currentStructure.Descriptor.ClrType.Name, newContainerType, newContainerType)); } var conditionPaths = candidates.SelectMany(p => p.Conditions.Select(c => c.Path)).Distinct().ToArray(); //if (conditionPaths.Length != 1) { // throw new EdiException("More than one properties on type '{0}' have the '{1}' attribute but the 'Condition' attribute has a different search path declared." // .FormatWith(CultureInfo.InvariantCulture, currentStructure.Descriptor.ClrType.Name, newContainerType)); //} var cache = currentStructure.CachedReads; var findingsPerPath = new Dictionary <string, string>(); foreach (var path in conditionPaths) { // search the cache first. var value = default(string); var found = false; if (cache.Count > 0) { var entry = cache.Where(r => r.Path == path).SingleOrDefault(); found = !default(EdiEntry).Equals(entry); } if (!found) { // if nothing found search the reader (arvance forward). do { if (reader.Path == path) { value = reader.ReadAsString(); cache.Enqueue(new EdiEntry(reader.Path, reader.TokenType, value)); found = true; value = reader.Value as string; // if found break; break; } else { reader.Read(); cache.Enqueue(new EdiEntry(reader.Path, reader.TokenType, reader.Value as string)); } } while (!found || reader.TokenType != EdiToken.SegmentStart); } if (found) { var property = candidates.SingleOrDefault(p => p.PathInfo.PathInternal == path && p.Conditions.Any(c => c.SatisfiedBy(value))); if (property != null) { return(property); } } } return(null); }
/// <summary> /// Tries the create container. /// </summary> /// <returns><c>true</c>, if create container was tryed, <c>false</c> otherwise.</returns> /// <param name="reader">Reader.</param> /// <param name="stack">Stack.</param> /// <param name="newContainer">New container.</param> internal bool TryCreateContainer(EdiReader reader, Stack <EdiStructure> stack, EdiStructureType newContainer) { var index = 0; if (stack.Count == 0) { return(false); } if (newContainer == EdiStructureType.SegmentGroup && stack.Peek().Container >= EdiStructureType.SegmentGroup) { // strict hierarchy while (stack.Peek().Container > newContainer) { var previous = stack.Pop(); // close this level } // nested hierarchy foreach (var level in stack) { if (!level.IsGroup) { continue; } var groupStart = level.Descriptor.SegmentGroupInfo.StartInternal; var sequenceEnd = level.Descriptor.SegmentGroupInfo.SequenceEndInternal; if (reader.Value.Equals(groupStart.Segment)) { level.Close(); // Close this level index = level.Index + 1; break; } else if (reader.Value.Equals(sequenceEnd.Segment)) { level.Close(); // Close this level break; } } if (stack.Any(s => s.IsClosed)) { var previous = stack.Peek(); do { previous = stack.Pop(); }while (!previous.IsClosed); } } else { // strict hierarchy while (stack.Peek().Container >= newContainer) { var previous = stack.Pop(); // close this level if (previous.Container == newContainer) { index = previous.Index + 1; // seed collection index } } } var current = stack.Peek(); var property = default(EdiPropertyDescriptor); switch (newContainer) { case EdiStructureType.SegmentGroup: property = FindForCurrentSegment(reader, current, newContainer); break; case EdiStructureType.Segment: property = FindForCurrentSegment(reader, current, newContainer); break; case EdiStructureType.Element: property = FindForCurrentElement(reader, current, newContainer); break; default: property = FindForCurrentLogicalStructure(reader, current, newContainer); break; } if (property == null) { return(false); } object propValue = property.Info.GetValue(current.Instance, null); if (propValue == null) { if (property.Info.PropertyType.IsCollectionType()) { if (property.Info.PropertyType.IsArray) { var initalLength = 0; propValue = Activator.CreateInstance(property.Info.PropertyType, initalLength); } else { var baseType = typeof(List <>); var genericType = baseType.MakeGenericType(property.Info.PropertyType.GetGenericArguments().First()); propValue = Activator.CreateInstance(genericType); } } else { propValue = Activator.CreateInstance(property.Info.PropertyType); } property.Info.SetValue(current.Instance, propValue); } if (propValue is IList) { var itemType = default(Type); var item = default(object); if (property.Info.PropertyType.IsArray) { itemType = property.Info.PropertyType.GetElementType(); item = Activator.CreateInstance(itemType); var newArray = Array.CreateInstance(itemType, ((IList)propValue).Count + 1); var oldArray = ((Array)propValue); Array.Copy(oldArray, newArray, oldArray.Length); newArray.SetValue(item, newArray.Length - 1); property.Info.SetValue(current.Instance, newArray); } else { itemType = property.Info.PropertyType.GetGenericArguments().First(); item = Activator.CreateInstance(itemType); ((IList)propValue).Add(item); } propValue = item; } stack.Push(new EdiStructure(newContainer, propValue, index, current.CachedReads)); return(true); }
/// <summary> /// Allows the filtering of tokens based on the <see cref="EdiStructureType"/> they represent /// instead of the build in way using the corresponding CLR <seealso cref="Type"/>. /// </summary> /// <param name="attributes">The list of available attributes</param> /// <param name="container">This is the type of container we are searchig attributes for.</param> /// <returns>The attributes matching the <see cref="EdiStructureType"/></returns> public static IEnumerable <EdiAttribute> OfType(this IEnumerable <EdiAttribute> attributes, EdiStructureType container) { var typesToSearch = new Type[0]; switch (container) { case EdiStructureType.None: case EdiStructureType.Interchange: break; case EdiStructureType.Group: typesToSearch = new [] { typeof(EdiGroupAttribute) }; break; case EdiStructureType.Message: typesToSearch = new [] { typeof(EdiMessageAttribute) }; break; case EdiStructureType.SegmentGroup: typesToSearch = new [] { typeof(EdiSegmentGroupAttribute) }; break; case EdiStructureType.Segment: typesToSearch = new [] { typeof(EdiSegmentAttribute), typeof(EdiSegmentGroupAttribute) }; break; case EdiStructureType.Element: typesToSearch = new [] { typeof(EdiElementAttribute) }; break; default: break; } return(null == typesToSearch?Enumerable.Empty <EdiAttribute>() : attributes.Where(a => typesToSearch.Contains(a.GetType()))); }
private EdiPropertyDescriptor FindForCurrentSegment(EdiReader reader, EdiStructure currentStructure, EdiStructureType newContainerType) { currentStructure.CachedReads.Clear(); var candidates = currentStructure.GetMatchingProperties(newContainerType); if (candidates.Length == 0) { return null; } var property = default(EdiPropertyDescriptor); if (reader.TokenType == EdiToken.SegmentName) { var matches = candidates.Where(p => p.Segment.Equals(reader.Value)).ToArray(); if (matches.Length == 0) { property = null; } else if (matches.Length == 1 && matches[0].Conditions == null) { property = matches[0]; } else { property = ConditionalMatch(reader, currentStructure, newContainerType, matches); } } return property; }
public static IEnumerable <EdiAttribute> OfType(this IEnumerable <EdiAttribute> attributes, EdiStructureType container) { var typeToSearch = default(Type); switch (container) { case EdiStructureType.None: break; case EdiStructureType.Interchange: break; case EdiStructureType.Group: typeToSearch = typeof(EdiGroupAttribute); break; case EdiStructureType.Message: typeToSearch = typeof(EdiMessageAttribute); break; case EdiStructureType.Segment: typeToSearch = typeof(EdiSegmentAttribute); break; case EdiStructureType.Element: typeToSearch = typeof(EdiElementAttribute); break; default: break; } return(null == typeToSearch?Enumerable.Empty <EdiAttribute>() : attributes.Where(a => a.GetType().Equals(typeToSearch))); }
/// <summary> /// Creates a <see cref="EdiGeneratedAttribute"/>. Marks a value that the serializer should be generating. /// </summary> /// <param name="type">The type of an autogenerated value. Count, Position, Index.</param> /// <param name="scope">The scope of the autogenerated value. Interchange, Message etc.</param> public EdiGeneratedAttribute(EdiGeneratedType type, EdiStructureType scope) { _Type = type; _Scope = scope; }
private EdiPropertyDescriptor FindForCurrentSegment(EdiReader reader, EdiStructure currentStructure, EdiStructureType newContainerType) { var candidates = currentStructure.GetMatchingProperties(newContainerType); if (candidates.Length == 0) { return(null); } var property = default(EdiPropertyDescriptor); if (reader.TokenType == EdiToken.SegmentName || currentStructure.CachedReads.Count > 0) { var segmentName = reader.TokenType == EdiToken.SegmentName ? reader.Value : ((EdiPath)currentStructure.CachedReads.Peek().Path).Segment; var matches = candidates.Where(p => segmentName.Equals(p.Segment)).ToArray(); if (matches.Length == 0) { property = null; } else if (matches.Length == 1 && matches[0].Conditions == null) { property = matches[0]; } else { property = ConditionalMatch(reader, currentStructure, newContainerType, matches); } } return(property); }
private EdiPropertyDescriptor FindForCurrentLogicalStructure(EdiReader reader, EdiStructure currentStructure, EdiStructureType newContainerType) { var candidates = currentStructure.GetMatchingProperties(newContainerType); var property = default(EdiPropertyDescriptor); if (candidates.Length == 0) { return(null); } if (candidates.Length == 1 && candidates[0].Conditions == null) { property = candidates[0]; } else { property = ConditionalMatch(reader, currentStructure, newContainerType, candidates); } return(property); }
public EdiStructure(EdiStructureType structureType, object instance) : this(structureType, null, null, instance, 0, new Queue <EdiEntry>()) { }
public EdiStructure(EdiStructureType container, object instance) : this(container, instance, 0, new Queue<EdiEntry>()) { }
/// <summary> /// Initializes a new instance of the <see cref="T:indice.Edi.Serialization.EdiStructure"/> class. /// </summary> /// <param name="container">Container.</param> /// <param name="instance">Instance.</param> public EdiStructure(EdiStructureType container, object instance) : this(container, instance, 0, new Queue <EdiEntry>()) { }
public EdiStructure(EdiStructureType structureType, EdiStructure parent, EdiPropertyDescriptor property, object instance) : this(structureType, parent, property, instance, 0, new Queue <EdiEntry>()) { }
public EdiPropertyDescriptor[] GetMatchingProperties(EdiStructureType sructureType) => Descriptor.Properties.Where(p => p.Attributes.OfType(sructureType).Any()).ToArray();
private EdiPropertyDescriptor FindForCurrentElement(EdiReader reader, EdiStructure currentStructure, EdiStructureType newContainerType, out Queue <EdiEntry> elementReads) { elementReads = null; var candidates = currentStructure.GetMatchingProperties(newContainerType); if (candidates.Length == 0) { return(null); } var property = default(EdiPropertyDescriptor); if (reader.TokenType == EdiToken.ElementStart || currentStructure.CachedReads.Count > 0) { var elementPath = reader.TokenType == EdiToken.ElementStart ? reader.Path : ((EdiPath)currentStructure.CachedReads.Peek().Path).ToString("E"); var matches = candidates.Where(p => p.PathInfo.PathInternal.Equals(elementPath)).ToArray(); if (matches.Length == 0) { property = null; } else if (matches.Length == 1 && matches[0].Conditions == null) { property = matches[0]; } else { property = ConditionalMatch(reader, currentStructure, newContainerType, matches); } if (property != null) { elementReads = new Queue <EdiEntry>(); var parentCache = currentStructure.CachedReads; while (parentCache.Count > 0 && elementPath == ((EdiPath)parentCache.Peek().Path).ToString("E")) { elementReads.Enqueue(parentCache.Dequeue()); } //foreach (var item in currentStructure.CachedReads) { // if (elementPath == ((EdiPath)item.Path).ToString("E")) { // elementReads.Enqueue(item); // } //} } } return(property); }
private static EdiPropertyDescriptor ConditionalMatch(EdiReader reader, EdiStructure currentStructure, EdiStructureType newContainerType, params EdiPropertyDescriptor[] candidates) { if (!candidates.All(p => p.Conditions != null)) { throw new EdiException( "More than one properties on type '{0}' have the '{1}' attribute. Please add a 'Condition' attribute to all properties in order to discriminate where each {2} will go." .FormatWith(CultureInfo.InvariantCulture, currentStructure.Descriptor.ClrType.Name, newContainerType, newContainerType)); } var searchResults = SearchForward(reader, currentStructure.CachedReads, candidates.SelectMany(p => p.Conditions.Select(c => c.Path))); //if (searchResults.Length != 1) { // throw new EdiException("More than one properties on type '{0}' have the '{1}' attribute but the 'Condition' attribute has a different search path declared." // .FormatWith(CultureInfo.InvariantCulture, currentStructure.Descriptor.ClrType.Name, newContainerType)); //} var property = candidates.SingleOrDefault(p => p.ConditionStackMode == EdiConditionStackMode.All ? p.Conditions.All(c => c.SatisfiedBy(searchResults[c.PathInternal])) : p.Conditions.Any(c => c.SatisfiedBy(searchResults[c.PathInternal]))); if (property != null) { return(property); } return(null); }
internal bool TryCreateContainer(EdiReader reader, Stack <EdiStructure> stack, EdiStructureType newContainer) { var index = 0; if (stack.Count == 0) { return(false); } while (stack.Peek().Container >= newContainer) { var previous = stack.Pop(); if (previous.Container == newContainer) { index = previous.Index + 1; } } var current = stack.Peek(); var candidates = current.GetMatchingProperties(newContainer); var property = default(EdiPropertyDescriptor); if (newContainer == EdiStructureType.Segment) { property = FindForCurrentSegment(reader, current, newContainer); } else if (newContainer == EdiStructureType.Element) { property = FindForCurrentElement(reader, current, newContainer); } else { property = FindForCurrentLogicalStructure(reader, current, newContainer); } if (property == null) { return(false); } object propValue = property.Info.GetValue(current.Instance, null); if (propValue == null) { if (property.Info.PropertyType.IsCollectionType()) { if (property.Info.PropertyType.IsArray) { var initalLength = 0; propValue = Activator.CreateInstance(property.Info.PropertyType, initalLength); } else { var baseType = typeof(List <>); var genericType = baseType.MakeGenericType(property.Info.PropertyType.GetGenericArguments().First()); propValue = Activator.CreateInstance(genericType); } } else { propValue = Activator.CreateInstance(property.Info.PropertyType); } property.Info.SetValue(current.Instance, propValue); } if (propValue is IList) { var itemType = default(Type); var item = default(object); if (property.Info.PropertyType.IsArray) { itemType = property.Info.PropertyType.GetElementType(); item = Activator.CreateInstance(itemType); var newArray = Array.CreateInstance(itemType, ((IList)propValue).Count + 1); var oldArray = ((Array)propValue); Array.Copy(oldArray, newArray, oldArray.Length); newArray.SetValue(item, newArray.Length - 1); property.Info.SetValue(current.Instance, newArray); } else { itemType = property.Info.PropertyType.GetGenericArguments().First(); item = Activator.CreateInstance(itemType); ((IList)propValue).Add(item); } propValue = item; } stack.Push(new EdiStructure(newContainer, propValue, index, current.CachedReads)); return(true); }