private static List <ConceptType> CreateConceptTypesAndMembers(IEnumerable <Type> conceptInfoTypes)
        {
            IncludeBaseConceptTypes(ref conceptInfoTypes);

            var types = conceptInfoTypes
                        .Distinct()
                        .ToDictionary(
                conceptInfoType => conceptInfoType,
                conceptInfoType => CreateConceptTypePartial(conceptInfoType));

            foreach (var type in types)
            {
                type.Value.Members = ConceptMembers.Get(type.Key)
                                     .Select(conceptMember =>
                {
                    var memberSyntax = new ConceptMemberSyntax();
                    ConceptMemberBase.Copy(conceptMember, memberSyntax);
                    memberSyntax.ConceptType = memberSyntax.IsConceptInfo && !memberSyntax.IsConceptInfoInterface
                            ? types.GetValue(conceptMember.ValueType,
                                             $"{nameof(DslSyntaxFromPlugins)} does not contain concept type '{conceptMember.ValueType}'," +
                                             $" referenced by {type.Key}.{conceptMember.Name}.")
                            : null;
                    return(memberSyntax);
                })
                                     .ToList();

                type.Value.BaseTypes = GetBaseConceptInfoTypes(type.Key)
                                       .Select(baseType => types[baseType])
                                       .ToList();
            }

            return(types.Values.OrderBy(conceptType => conceptType.AssemblyQualifiedName).ToList());
        }
        private void AppendMember(StringBuilder text, ConceptMemberSyntax member)
        {
            object memberValue = member.GetMemberValue(this);

            if (memberValue == null)
            {
                text.Append("<null>");
            }
            else if (member.IsConceptInfo)
            {
                var value = (ConceptSyntaxNode)memberValue;
                if (member.IsConceptInfoInterface)
                {
                    text.Append(value.Concept.GetRootTypeName()).Append(':');
                }
                value.AppendKeyMembers(text);
            }
            else if (member.IsStringType)
            {
                ConceptMemberHelper.AppendWithQuotesIfNeeded(text, (string)memberValue);
            }
            else
            {
                throw new FrameworkException(
                          $"{nameof(ConceptSyntaxNode)} member {member.Name} of type {member.ConceptType?.TypeName} in {Concept.TypeName} is not supported.");
            }
        }
예제 #3
0
        private ValueOrError <object> ReadMemberValue(ConceptMemberSyntax member, ITokenReader tokenReader, ConceptSyntaxNode 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.Concept.TypeName}. Trying to read {_conceptType.TypeName}."));
                    }

                    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 {_conceptType.Keyword} statement. {((TokenReader)tokenReader).ReportPosition()}.");
                        }
                    }

                    return(tokenReader.ReadText().ChangeType <object>());
                }

                if (member.IsConceptInfoInterface)
                {
                    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 the 'member' is not IsStringType (see above) and not IsConceptInfoInterface (see above), then it is a concrete IConceptInfo implementation type, and member.ConceptType should not be null.
                if (member.ConceptType == null)
                {
                    throw new InvalidOperationException($"If the member type is not string or interface, it should have a ConceptType value. Member '{member.Name}' while reading {_conceptType.TypeName}.");
                }

                if (useLastConcept != null && member.ConceptType.IsInstanceOfType(useLastConcept))
                {
                    return((object)useLastConcept);
                }

                if (member.IsConceptInfo && firstMember && !readingAReference && _conceptType.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 = member.ConceptType.Members.Where(m => m.IsParsable).ToArray();
                    if (parentMembers.Length == 1 && parentMembers.Single().IsConceptInfo)
                    {
                        return(ValueOrError.CreateError($"{_conceptType.GetKeywordOrTypeName()} must be nested" +
                                                        $" within the referenced parent concept {member.ConceptType.GetKeywordOrTypeName()}." +
                                                        $" 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.ConceptType == _conceptType)
                    {
                        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.",
                                                            _conceptType.TypeName, member.Name)));
                    }

                    var subParser = new GenericParser(member.ConceptType);
                    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.ConceptType.TypeName)));
            }
            catch (DslSyntaxException ex)
            {
                return(ValueOrError <object> .CreateError(ex.Message));
            }
        }