public UnresolvedReference(ConceptDescription dependant, ConceptMember referenceMember) { Dependant = dependant; Member = referenceMember; ReferencedStub = (IConceptInfo)Member.GetValue(Dependant.Concept); ReferencedKey = ReferencedStub?.GetKey(); }
private static void AppendMember(StringBuilder text, IConceptInfo ci, ConceptMember member, bool exceptionOnNullMember) { object memberValue = member.GetValue(ci); if (memberValue == null) { if (exceptionOnNullMember) { throw new DslSyntaxException(ci, string.Format( "{0}'s property {1} is null. Info: {2}.", ci.GetType().Name, member.Name, ci.GetErrorDescription())); } else { text.Append("<null>"); } } else if (member.IsConceptInfo) { IConceptInfo value = (IConceptInfo)member.GetValue(ci); if (member.ValueType == typeof(IConceptInfo)) { text.Append(BaseConceptInfoType(value).Name).Append(":"); } AppendMembers(text, value, SerializationOptions.KeyMembers, exceptionOnNullMember); } else if (member.ValueType == typeof(string)) { text.Append(SafeDelimit(member.GetValue(ci).ToString())); } else { throw new FrameworkException(string.Format( "IConceptInfo member {0} of type {1} in {2} is not supported.", member.Name, member.ValueType.Name, ci.GetType().Name)); } }
private bool IsConceptMemberEqual(IConceptInfo newConcept, IConceptInfo existingConcept, ConceptMember conceptMemeber) { if (conceptMemeber.IsConceptInfo) { if ((conceptMemeber.GetValue(existingConcept) as IConceptInfo).GetKey() != (conceptMemeber.GetValue(newConcept) as IConceptInfo).GetKey()) { return(false); } else { return(true); } } else { var value1 = conceptMemeber.GetValue(existingConcept); var value2 = conceptMemeber.GetValue(newConcept); if (value1 == null) { return(value2 == null); } else if (value2 == null) { return(false); } else { return(value1.Equals(value2)); } } }
private static void AppendMember(StringBuilder text, IConceptInfo ci, ConceptMember member, bool exceptionOnNullMember) { object memberValue = member.GetValue(ci); if (memberValue == null) if (exceptionOnNullMember) throw new DslSyntaxException(ci, string.Format( "{0}'s property {1} is null. Info: {2}.", ci.GetType().Name, member.Name, ci.GetErrorDescription())); else text.Append("<null>"); else if (member.IsConceptInfo) { IConceptInfo value = (IConceptInfo)member.GetValue(ci); if (member.ValueType == typeof(IConceptInfo)) text.Append(BaseConceptInfoType(value).Name).Append(":"); AppendMembers(text, value, SerializationOptions.KeyMembers, exceptionOnNullMember); } else if (member.ValueType == typeof(string)) text.Append(SafeDelimit(member.GetValue(ci).ToString())); else throw new FrameworkException(string.Format( "IConceptInfo member {0} of type {1} in {2} is not supported.", member.Name, member.ValueType.Name, ci.GetType().Name)); }
public ValueOrError<object> ReadMemberValue(ConceptMember member, ITokenReader tokenReader, IConceptInfo lastConcept, bool firstMember, ref bool lastPropertyWasInlineParent, ref bool lastConceptUsed, bool readingAReference) { try { if (lastPropertyWasInlineParent && member.IsKey && !member.IsConceptInfo) // TODO: Removing "IsConceptInfo" from this condition would produce a mismatch. Think of a better solution for parsing the concept key. { if (!tokenReader.TryRead(".")) return ValueOrError<object>.CreateError(string.Format( "Parent property and the following key value ({0}) must be separated with a dot. Expected \".\"", member.Name)); } lastPropertyWasInlineParent = false; if (member.IsStringType) return tokenReader.ReadText(); if (member.ValueType == typeof(IConceptInfo)) if (firstMember && lastConcept != null) { lastConceptUsed = true; return (object)lastConcept; } else return ValueOrError<object>.CreateError("Member of type IConceptInfo can only be used as a first member and enclosed within the referenced parent concept."); if (member.IsConceptInfo && lastConcept != null && member.ValueType.IsInstanceOfType(lastConcept) && member.ValueType.IsAssignableFrom(ConceptInfoType)) // Recursive "parent" property { lastConceptUsed = true; return (object)lastConcept; } if (member.IsConceptInfo && lastConcept != null && member.ValueType.IsInstanceOfType(lastConcept) && firstMember) { lastConceptUsed = true; return (object)lastConcept; } if (member.IsConceptInfo && firstMember) { if (member.ValueType == ConceptInfoType) return ValueOrError.CreateError(string.Format( "Recursive concept {0} cannot be used as a root because its parent property ({1}) must reference another concept. Use a non-recursive concept for the root and a derivation of the root concept with additional parent property as a recursive concept.", ConceptInfoHelper.GetKeywordOrTypeName(ConceptInfoType), member.Name)); if (!readingAReference && Members.Where(m => m.IsParsable).Count() == 1) { // This validation is not necessary for consistent parsing. It is enforced simply to avoid ambiguity when parsing // similar concepts such as "Logging { AllProperties; }", "History { AllProperties; }" and "Persisted { AllProperties; }". var parentMembers = ConceptMembers.Get(member.ValueType).Where(m => m.IsParsable).ToArray(); if (parentMembers.Count() == 1 && parentMembers.Single().IsConceptInfo) return ValueOrError.CreateError(string.Format( "{0} must be enclosed within the referenced parent concept {1}. A single-reference concept that references another single-reference concept must always be used with embedded syntax to avoid ambiguity.", ConceptInfoHelper.GetKeywordOrTypeName(ConceptInfoType), ConceptInfoHelper.GetKeywordOrTypeName(member.ValueType))); } GenericParser subParser = new GenericParser(member.ValueType, ""); lastConceptUsed = true; lastPropertyWasInlineParent = true; return subParser.ParseMembers(tokenReader, lastConcept, true).ChangeType<object>(); } if (member.IsConceptInfo) { GenericParser subParser = new GenericParser(member.ValueType, ""); return subParser.ParseMembers(tokenReader, null, true).ChangeType<object>(); } return ValueOrError.CreateError(string.Format( "GenericParser does not support members of type \"{0}\". Try using string or implementation of IConceptInfo.", member.ValueType.Name)); } catch (DslSyntaxException ex) { return ValueOrError<object>.CreateError(ex.Message); } }
public ValueOrError <object> ReadMemberValue(ConceptMember member, ITokenReader tokenReader, IConceptInfo lastConcept, bool firstMember, ref bool lastPropertyWasInlineParent, ref bool lastConceptUsed, bool readingAReference) { try { if (lastPropertyWasInlineParent && member.IsKey && !member.IsConceptInfo) // TODO: Removing "IsConceptInfo" from this condition would produce a mismatch. Think of a better solution for parsing the concept key. { if (!tokenReader.TryRead(".")) { return(ValueOrError <object> .CreateError(string.Format( "Parent property and the following key value ({0}) must be separated with a dot. Expected \".\"", member.Name))); } } lastPropertyWasInlineParent = false; if (member.IsStringType) { return(tokenReader.ReadText().ChangeType <object>()); } if (member.ValueType == typeof(IConceptInfo)) { if (firstMember && lastConcept != null) { lastConceptUsed = true; return((object)lastConcept); } else { return(ValueOrError <object> .CreateError("Member of type IConceptInfo can only be used as a first member and enclosed within the referenced parent concept.")); } } if (member.IsConceptInfo && lastConcept != null && member.ValueType.IsInstanceOfType(lastConcept) && member.ValueType.IsAssignableFrom(ConceptInfoType)) // Recursive "parent" property { lastConceptUsed = true; return((object)lastConcept); } if (member.IsConceptInfo && lastConcept != null && member.ValueType.IsInstanceOfType(lastConcept) && firstMember) { lastConceptUsed = true; return((object)lastConcept); } if (member.IsConceptInfo && firstMember) { if (member.ValueType == ConceptInfoType) { return(ValueOrError.CreateError(string.Format( "Recursive concept {0} cannot be used as a root because its parent property ({1}) must reference another concept. Use a non-recursive concept for the root and a derivation of the root concept with additional parent property as a recursive concept.", ConceptInfoHelper.GetKeywordOrTypeName(ConceptInfoType), member.Name))); } if (!readingAReference && Members.Where(m => m.IsParsable).Count() == 1) { // This validation is not necessary for consistent parsing. It is enforced simply to avoid ambiguity when parsing // similar concepts such as "Logging { AllProperties; }", "History { AllProperties; }" and "Persisted { AllProperties; }". var parentMembers = ConceptMembers.Get(member.ValueType).Where(m => m.IsParsable).ToArray(); if (parentMembers.Count() == 1 && parentMembers.Single().IsConceptInfo) { return(ValueOrError.CreateError(string.Format( "{0} must be enclosed within the referenced parent concept {1}. A single-reference concept that references another single-reference concept must always be used with embedded syntax to avoid ambiguity.", ConceptInfoHelper.GetKeywordOrTypeName(ConceptInfoType), ConceptInfoHelper.GetKeywordOrTypeName(member.ValueType)))); } } GenericParser subParser = new GenericParser(member.ValueType, ""); lastConceptUsed = true; lastPropertyWasInlineParent = true; return(subParser.ParseMembers(tokenReader, lastConcept, true).ChangeType <object>()); } if (member.IsConceptInfo) { GenericParser subParser = new GenericParser(member.ValueType, ""); return(subParser.ParseMembers(tokenReader, null, true).ChangeType <object>()); } return(ValueOrError.CreateError(string.Format( "GenericParser does not support members of type \"{0}\". Try using string or implementation of IConceptInfo.", member.ValueType.Name))); } catch (DslSyntaxException ex) { return(ValueOrError <object> .CreateError(ex.Message)); } }
private ValueOrError <object> ReadMemberValue(ConceptMember member, ITokenReader tokenReader, IConceptInfo useLastConcept, bool firstMember, ref bool parsedFirstReferenceElement, bool readingAReference) { try { if (member.IsStringType) { if (useLastConcept != null) { return(ValueOrError <object> .CreateError($"This concept cannot be nested within {useLastConcept.GetType().Name}. Trying to read {ConceptInfoType.Name}.")); } if (readingAReference && parsedFirstReferenceElement) { if (!tokenReader.TryRead(".")) { return(ValueOrError <object> .CreateError(string.Format( "Parent property and the following key value ({0}) must be separated with a dot. Expected \".\"", member.Name))); } } parsedFirstReferenceElement = true; // Legacy syntax: if (!readingAReference && member.IsKey && member.IsStringType && !firstMember) { if (tokenReader.TryRead(".")) { AddWarning($"Obsolete syntax: Remove '.' from {Keyword} statement. {((TokenReader)tokenReader).ReportPosition()}."); } } return(tokenReader.ReadText().ChangeType <object>()); } if (member.ValueType == typeof(IConceptInfo)) { if (useLastConcept != null) { return((object)useLastConcept); } else { return(ValueOrError <object> .CreateError($"Member of type IConceptInfo can only be nested within" + $" the referenced parent concept. It must be a first member or marked with {nameof(ConceptParentAttribute)}.")); } } if (useLastConcept != null && member.ValueType.IsInstanceOfType(useLastConcept)) { return((object)useLastConcept); } if (member.IsConceptInfo && firstMember && !readingAReference && Members.Count(m => m.IsParsable) == 1) { // This validation is not necessary for consistent parsing. It is enforced simply to avoid ambiguity for future concept overloads // when parsing similar concepts such as "Logging { AllProperties; }", "History { AllProperties; }" and "Persisted { AllProperties; }". var parentMembers = ConceptMembers.Get(member.ValueType).Where(m => m.IsParsable).ToArray(); if (parentMembers.Count() == 1 && parentMembers.Single().IsConceptInfo) { return(ValueOrError.CreateError($"{ConceptInfoHelper.GetKeywordOrTypeName(ConceptInfoType)} must be nested" + $" within the referenced parent concept {ConceptInfoHelper.GetKeywordOrTypeName(member.ValueType)}." + $" A single-reference concept that references another single-reference concept must always be used with nested syntax to avoid ambiguity.")); } } if (member.IsConceptInfo) { if (firstMember && member.ValueType == ConceptInfoType) { return(ValueOrError.CreateError(string.Format( "Recursive concept {0} cannot be used as a root because its parent property ({1}) must reference another concept. Use a non-recursive concept for the root and a derivation of the root concept with additional parent property as a recursive concept.", ConceptInfoHelper.GetKeywordOrTypeName(ConceptInfoType), member.Name))); } GenericParser subParser = new GenericParser(member.ValueType, ""); return(subParser.ParseMembers(tokenReader, useLastConcept, true, ref parsedFirstReferenceElement).ChangeType <object>()); } return(ValueOrError.CreateError(string.Format( "GenericParser does not support members of type \"{0}\". Try using string or implementation of IConceptInfo.", member.ValueType.Name))); } catch (DslSyntaxException ex) { return(ValueOrError <object> .CreateError(ex.Message)); } }