Exemple #1
0
        public static IConceptInfo ConvertNodeToConceptInfo(ConceptSyntaxNode node, Dictionary <ConceptSyntaxNode, IConceptInfo> keepReferences = null)
        {
            Type conceptInfoType = Type.GetType(node.Concept.AssemblyQualifiedName);

            if (conceptInfoType == null)
            {
                throw new ArgumentException($"Cannot find concept type '{node.Concept.AssemblyQualifiedName}'.");
            }

            var ci      = (IConceptInfo)Activator.CreateInstance(conceptInfoType);
            var members = ConceptMembers.Get(conceptInfoType);

            if (node.Parameters.Length != members.Length)
            {
                throw new InvalidOperationException(
                          $"{nameof(ConceptSyntaxNode)} parameters count ({node.Parameters.Length})" +
                          $" does not match {nameof(ConceptMembers)} count ({members.Length}).");
            }

            for (int m = 0; m < members.Length; m++)
            {
                var    member = members[m];
                object value  = node.Parameters[m];

                if (value == null)
                {
                    member.SetMemberValue(ci, null);
                }
                else if (value is string)
                {
                    member.SetMemberValue(ci, value);
                }
                else if (value is ConceptSyntaxNode referencedNode)
                {
                    IConceptInfo referencedConceptInfo;
                    if (keepReferences == null || !keepReferences.TryGetValue(referencedNode, out referencedConceptInfo))
                    {
                        referencedConceptInfo = ConvertNodeToConceptInfo(referencedNode, keepReferences);
                    }

                    member.SetMemberValue(ci, referencedConceptInfo);
                }
                else
                {
                    var valueType = value?.GetType().Name ?? "null";
                    throw new ArgumentException($"Value type {valueType} is not expected in {nameof(ConceptSyntaxNode)} parameter {node.Concept.TypeName}.{member.Name}.");
                }
            }

            if (keepReferences != null)
            {
                keepReferences[node] = ci;
            }
            return(ci);
        }
Exemple #2
0
        private string ReportPreviousConcept(ConceptSyntaxNode node)
        {
            var sb = new StringBuilder();

            if (node != null)
            {
                sb.AppendFormat("Previous concept: {0}", node.GetUserDescription()).AppendLine();

                foreach (var m in node.Concept.Members)
                {
                    sb.AppendFormat("Property '{0}' ({1}) = {2}",
                                    m.Name,
                                    m.IsStringType ? "string" : m.IsConceptInfoInterface ? "IConceptInfo" : m.ConceptType?.TypeName,
                                    m.GetMemberValue(node)?.ToString() ?? "<null>")
                    .AppendLine();
                }
            }
            return(sb.ToString());
        }
        private ValueOrError <ConceptSyntaxNode> ParseMembers(ITokenReader tokenReader, ConceptSyntaxNode useLastConcept, bool readingAReference, ref bool parsedFirstReferenceElement)
        {
            ConceptSyntaxNode node = new ConceptSyntaxNode(_conceptType);
            bool firstMember       = true;

            var listOfMembers = readingAReference ? _conceptType.Members.Where(m => m.IsKey) : _conceptType.Members.Where(m => m.IsParsable);

            var parentProperty = GetParentProperty(listOfMembers);

            if (useLastConcept != null && parentProperty == null)
            {
                return(ValueOrError <ConceptSyntaxNode> .CreateError($"This concept cannot be nested within {useLastConcept.Concept.TypeName}. Trying to read {_conceptType.TypeName}."));
            }

            foreach (ConceptMemberSyntax member in listOfMembers)
            {
                if (!readingAReference)
                {
                    parsedFirstReferenceElement = false; // Reset a reference elements group, that should separated by dot.
                }
                var valueOrError = ReadMemberValue(member, tokenReader, member == parentProperty ? useLastConcept : null, firstMember, ref parsedFirstReferenceElement, readingAReference);
                OnMemberRead?.Invoke(tokenReader, node, member, valueOrError);

                if (valueOrError.IsError)
                {
                    return(ValueOrError <ConceptSyntaxNode> .CreateError(string.Format(CultureInfo.InvariantCulture,
                                                                                       "Cannot read the value of {0} in {1}. {2}",
                                                                                       member.Name, _conceptType.TypeName, valueOrError.Error)));
                }

                member.SetMemberValue(node, valueOrError.Value);
                firstMember = false;
            }

            return(ValueOrError <ConceptSyntaxNode> .CreateValue(node));
        }
        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));
            }
        }
Exemple #5
0
        private void UpdateContextForNextConcept(TokenReader tokenReader, Stack <ConceptSyntaxNode> context, ConceptSyntaxNode conceptInfo)
        {
            if (tokenReader.TryRead("{"))
            {
                context.Push(conceptInfo);
                OnUpdateContext?.Invoke(tokenReader, context, true);
            }
            else if (!tokenReader.TryRead(";"))
            {
                var(dslScript, position) = tokenReader.GetPositionInScript();
                throw new DslSyntaxException("Expected \";\" or \"{\".",
                                             "RH0001", dslScript, position, 0, ReportPreviousConcept(conceptInfo));
            }

            while (tokenReader.TryRead("}"))
            {
                if (context.Count == 0)
                {
                    var simpleMessage = "Unexpected \"}\".";
                    var(dslScript, position) = tokenReader.GetPositionInScript();
                    throw new DslSyntaxException(simpleMessage, "RH0007", dslScript, position, 0, null);
                }
                context.Pop();
                OnUpdateContext?.Invoke(tokenReader, context, false);
            }
        }
 /// <summary>
 /// Determines whether the specified syntax node is an instance of the current (base property) concept type.
 /// </summary>
 /// <remarks>
 /// Before calling this method, check if the base property has <see cref="ConceptMemberSyntax.ConceptType"/> set,
 /// to avoid null reference exception.
 /// In case of the null value, consider including the base properties with <see cref="ConceptMemberBase.IsConceptInfoInterface"/> set,
 /// because any derived concept type can also be assigned to the base property of type <see cref="IConceptInfo"/>.
 /// </remarks>
 public bool IsInstanceOfType(ConceptSyntaxNode derivedTypeNode)
 {
     return(IsAssignableFrom(derivedTypeNode.Concept));
 }
Exemple #7
0
 public object GetMemberValue(ConceptSyntaxNode node)
 {
     return(node.Parameters[Index]);
 }
Exemple #8
0
 public void SetMemberValue(ConceptSyntaxNode node, object value)
 {
     node.Parameters[Index] = value;
 }