コード例 #1
0
        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());
        }
コード例 #2
0
        private bool ConteptsValueEqualOrBase(IConceptInfo newConcept, IConceptInfo existingConcept)
        {
            if (object.ReferenceEquals(newConcept, existingConcept))
            {
                return(true);
            }
            else if (newConcept.GetKey() != existingConcept.GetKey())
            {
                return(false);
            }
            else if (!newConcept.GetType().IsInstanceOfType(existingConcept))
            {
                return(false);
            }
            else
            {
                var newConceptMemebers = ConceptMembers.Get(newConcept);
                foreach (ConceptMember member in newConceptMemebers)
                {
                    if (member.IsKey)
                    {
                        continue;
                    }

                    if (!IsConceptMemberEqual(newConcept, existingConcept, member))
                    {
                        return(false);
                    }
                }
            }
            return(true);
        }
コード例 #3
0
        /// <summary>
        /// Use only for generating an error details. Returns the concept's description ignoring possible null reference errors.
        /// </summary>
        public static string GetErrorDescription(this IConceptInfo ci)
        {
            if (ci == null)
            {
                return("<null>");
            }
            var report = new StringBuilder();

            report.Append(ci.GetType().FullName);
            foreach (var member in ConceptMembers.Get(ci))
            {
                report.Append(" " + member.Name + "=");
                var memberValue = member.GetValue(ci);
                try
                {
                    if (memberValue == null)
                    {
                        report.Append("<null>");
                    }
                    else if (member.IsConceptInfo)
                    {
                        AppendMembers(report, (IConceptInfo)memberValue, SerializationOptions.KeyMembers, exceptionOnNullMember: false);
                    }
                    else
                    {
                        report.Append(memberValue.ToString());
                    }
                }
                catch (Exception ex)
                {
                    report.Append("<" + ex.GetType().Name + ">");
                }
            }
            return(report.ToString());
        }
コード例 #4
0
        public GenericParser(Type conceptInfoType, string keyword)
        {
            Contract.Requires(conceptInfoType != null);
            Contract.Requires(keyword != null);

            this.ConceptInfoType = conceptInfoType;
            this.Keyword         = keyword;
            Members = ConceptMembers.Get(conceptInfoType).ToArray();
        }
コード例 #5
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);
        }
コード例 #6
0
        private static List <Type> GetNestingOptions(Type conceptType)
        {
            var options = new List <Type>();

            while (!options.Contains(conceptType)) // Recursive concept are possible.
            {
                options.Add(conceptType);
                if (typeof(IConceptInfo).IsAssignableFrom(conceptType))
                {
                    conceptType = ConceptMembers.Get(conceptType).First().ValueType;
                }
            }
            return(options);
        }
コード例 #7
0
 private static void AddAllDependencies(IConceptInfo conceptInfo, ICollection <IConceptInfo> dependencies)
 {
     foreach (var member in ConceptMembers.Get(conceptInfo))
     {
         if (member.IsConceptInfo)
         {
             var dependency = (IConceptInfo)member.GetValue(conceptInfo);
             if (!dependencies.Contains(dependency))
             {
                 dependencies.Add(dependency);
                 AddAllDependencies(dependency, dependencies);
             }
         }
     }
 }
コード例 #8
0
        /// <summary>
        /// Since DSL parser and macro evaluators return stub references, this function replaces each reference with actual instance of the referenced concept.
        /// Note: This method could handle circular dependencies between the concepts, but for simplicity of the implementation this is currently not supported.
        /// </summary>
        private void ReplaceReferencesWithFullConcepts(ConceptDescription conceptDesc)
        {
            var references = ConceptMembers.Get(conceptDesc.Concept).Where(member => member.IsConceptInfo)
                             .Select(member => new UnresolvedReference(conceptDesc, member));

            foreach (var reference in references)
            {
                ReplaceReferenceWithFullConceptOrMarkUnresolved(reference);
            }

            if (conceptDesc.UnresolvedDependencies == 0)
            {
                MarkResolvedConcept(conceptDesc);
            }
        }
コード例 #9
0
        public void Add(IConceptInfo concept)
        {
            Type conceptType = concept.GetType();

            _subtypes.Add(conceptType);

            foreach (ConceptMember member in ConceptMembers.Get(concept))
            {
                if (member.IsConceptInfo)
                {
                    string referencedConceptKey = ((IConceptInfo)member.GetValue(concept)).GetKey();
                    _conceptsIndex.Add(referencedConceptKey, conceptType, member.Name, concept);
                }
            }
        }
コード例 #10
0
        private void Disambiguate(List <Interpretation> possibleInterpretations)
        {
            // Interpretation that covers most of the DSL script has priority,
            // because other interpretations are obviously missing some parameters,
            // otherwise the parser would stop earlier on '{' or ';'.
            int largest = possibleInterpretations.Max(i => i.NextPosition.PositionInTokenList);

            possibleInterpretations.RemoveAll(i => i.NextPosition.PositionInTokenList < largest);
            if (possibleInterpretations.Count == 1)
            {
                return;
            }

            // Interpretation with a flat syntax has priority over the interpretation
            // that could be placed in a nested concept.
            // The nested interpretation can be manually enforced in DSL script (if needed)
            // by nesting this concept.
            var interpretationParameters = possibleInterpretations
                                           .Select(i =>
            {
                var firstMemberType = ConceptMembers.Get(i.ConceptInfo).First().ValueType;
                return(new { Interpretation = i, FirstParameter = firstMemberType, NestingOptions = GetNestingOptions(firstMemberType) });
            })
                                           .ToList();
            var couldBeNested = new HashSet <Interpretation>();

            foreach (var i1 in interpretationParameters)
            {
                foreach (var i2 in interpretationParameters)
                {
                    if (i1 != i2 && i2.NestingOptions.Skip(1).Contains(i1.FirstParameter))
                    {
                        couldBeNested.Add(i2.Interpretation);
                        _logger.Trace(() => $"Interpretation {i1.Interpretation.ConceptInfo.GetType().Name}" +
                                      $" has priority over {i2.Interpretation.ConceptInfo.GetType().Name}," +
                                      $" because the second one could be nested in {i2.FirstParameter.Name} to force that interpretation." +
                                      $" Statement: {i1.Interpretation.ConceptInfo.GetUserDescription()}.");
                    }
                }
            }
            var flatestInterpretations = possibleInterpretations.Except(couldBeNested).ToList();

            if (flatestInterpretations.Count == 1)
            {
                possibleInterpretations.Clear();
                possibleInterpretations.Add(flatestInterpretations.Single());
            }
        }
コード例 #11
0
 private static void AddReferencesBeforeConcept(IConceptInfo concept, List <IConceptInfo> sortedList, Dictionary <IConceptInfo, bool> processed)
 {
     if (!processed.ContainsKey(concept))
     {
         throw new FrameworkException(string.Format(
                                          "Unexpected inner state: Referenced concept {0} is not found in list of all concepts.",
                                          concept.GetUserDescription()));
     }
     if (processed[concept]) // eliminates duplication of referenced concepts and stops circular references from infinite recursion
     {
         return;
     }
     processed[concept] = true;
     foreach (ConceptMember member in ConceptMembers.Get(concept))
     {
         if (member.IsConceptInfo)
         {
             AddReferencesBeforeConcept((IConceptInfo)member.GetValue(concept), sortedList, processed);
         }
     }
     sortedList.Add(concept);
 }
コード例 #12
0
        private static void AppendMembers(StringBuilder text, IConceptInfo ci, SerializationOptions serializationOptions, bool exceptionOnNullMember = false, Type asBaseConceptType = null)
        {
            var members = asBaseConceptType != null?ConceptMembers.Get(asBaseConceptType) : ConceptMembers.Get(ci);

            bool firstMember = true;

            for (int m = 0; m < members.Length; m++)
            {
                var member = members[m];
                if (serializationOptions == SerializationOptions.AllMembers || member.IsKey)
                {
                    string separator = member.IsKey ? "." : " ";
                    if (!firstMember)
                    {
                        text.Append(separator);
                    }
                    firstMember = false;

                    AppendMember(text, ci, member, exceptionOnNullMember);
                }
            }
        }
コード例 #13
0
        /// <summary>
        /// Since DSL parser returns stub references, this function replaces each reference with actual instance of the referenced concept.
        /// Function returns concepts that have newly resolved references.
        /// Note: This method could handle circular dependencies between the concepts, but for simplicity of the implementation this is currently not supported.
        /// </summary>
        private List <IConceptInfo> ReplaceReferencesWithFullConcepts(IEnumerable <ConceptDescription> newConceptsDesc)
        {
            var newlyResolved = new List <IConceptInfo>();

            foreach (var conceptDesc in newConceptsDesc)
            {
                var references = ConceptMembers.Get(conceptDesc.Concept).Where(member => member.IsConceptInfo)
                                 .Select(member => new UnresolvedReference(conceptDesc, member));

                foreach (var reference in references)
                {
                    ReplaceReferenceWithFullConceptOrMarkUnresolved(reference);
                }

                if (conceptDesc.UnresolvedDependencies == 0)
                {
                    newlyResolved.Add(conceptDesc.Concept);
                    newlyResolved.AddRange(MarkResolvedConcept(conceptDesc));
                }
            }

            return(newlyResolved);
        }
コード例 #14
0
        private static void AppendMembers(StringBuilder text, IConceptInfo ci, SerializationOptions serializationOptions, bool exceptionOnNullMember = false, Type asBaseConceptType = null)
        {
            IEnumerable <ConceptMember> members = ConceptMembers.Get(asBaseConceptType ?? ci.GetType());

            if (serializationOptions == SerializationOptions.KeyMembers)
            {
                members = members.Where(member => member.IsKey);
            }

            bool firstMember = true;

            foreach (ConceptMember member in members)
            {
                string separator = member.IsKey ? "." : " ";
                if (!firstMember)
                {
                    text.Append(separator);
                }
                firstMember = false;

                AppendMember(text, ci, member, exceptionOnNullMember);
            }
        }
コード例 #15
0
 /// <summary>
 /// Returns a list of concepts that this concept directly depends on.
 /// </summary>
 public static IEnumerable <IConceptInfo> GetDirectDependencies(this IConceptInfo conceptInfo)
 {
     return((from member in ConceptMembers.Get(conceptInfo)
             where member.IsConceptInfo
             select(IConceptInfo) member.GetValue(conceptInfo)).Distinct().ToList());
 }
コード例 #16
0
ファイル: GenericParser.cs プロジェクト: TinOroz/Rhetos
 public GenericParser(Type conceptInfoType, string keyword)
 {
     ConceptInfoType = conceptInfoType;
     Keyword         = keyword;
     Members         = ConceptMembers.Get(conceptInfoType).ToArray();
 }
コード例 #17
0
ファイル: DslContainer.cs プロジェクト: tjakopovic/Rhetos
        /// <summary>
        /// Since DSL parser returns stub references, this function replaces each reference with actual instance of the referenced concept.
        /// Function returns concepts that have newly resolved references.
        /// </summary>
        private IEnumerable <IConceptInfo> ReplaceReferencesWithFullConcepts(bool errorOnUnresolvedReference)
        {
            var dependencies  = new List <Tuple <string, string> >();
            var newUnresolved = new List <string>();

            foreach (var concept in _unresolvedConceptsByKey)
            {
                foreach (ConceptMember member in ConceptMembers.Get(concept.Value))
                {
                    if (member.IsConceptInfo)
                    {
                        var reference = (IConceptInfo)member.GetValue(concept.Value);

                        if (reference == null)
                        {
                            string errorMessage = "Property '" + member.Name + "' is not initialized.";
                            if (concept.Value is IAlternativeInitializationConcept)
                            {
                                errorMessage = errorMessage + string.Format(
                                    " Check if the InitializeNonparsableProperties function of IAlternativeInitializationConcept implementation at {0} is implemented properly.",
                                    concept.Value.GetType().Name);
                            }
                            throw new DslSyntaxException(concept.Value, errorMessage);
                        }

                        string referencedKey = reference.GetKey();

                        dependencies.Add(Tuple.Create(referencedKey, concept.Key));

                        IConceptInfo referencedConcept;
                        if (!_resolvedConceptsByKey.TryGetValue(referencedKey, out referencedConcept) &&
                            !_unresolvedConceptsByKey.TryGetValue(referencedKey, out referencedConcept))
                        {
                            if (errorOnUnresolvedReference)
                            {
                                throw new DslSyntaxException(concept.Value, string.Format(
                                                                 "Referenced concept is not defined in DSL scripts: '{0}'.",
                                                                 reference.GetUserDescription()));
                            }

                            newUnresolved.Add(concept.Key);
                        }
                        else
                        {
                            member.SetMemberValue(concept.Value, referencedConcept);
                        }
                    }
                }
            }

            // Unresolved concepts should alse include any concept with resolved references that references an unresolved concept.
            newUnresolved = Graph.IncludeDependents(newUnresolved, dependencies);

            var unresolvedIndex = new HashSet <string>(newUnresolved);
            var newlyResolved   = _unresolvedConceptsByKey
                                  .Where(concept => !unresolvedIndex.Contains(concept.Key))
                                  .ToList();

            foreach (var concept in newlyResolved)
            {
                _logger.Trace(() => "New concept with resolved references: " + concept.Key);

                _unresolvedConceptsByKey.Remove(concept.Key);

                _resolvedConcepts.Add(concept.Value);
                _resolvedConceptsByKey.Add(concept.Key, concept.Value);

                foreach (var index in _dslModelIndexes)
                {
                    index.Add(concept.Value);
                }
            }

            return(newlyResolved.Select(concept => concept.Value));
        }
コード例 #18
0
        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));
            }
        }
コード例 #19
0
        /// <summary>
        /// This method sorts concepts so that if concept A references concept B,
        /// in the resulting list B will be positioned somewhere before A.
        /// This will allow code generators to safely assume that the code for referenced concept B
        /// is already generated before the concept A inserts an additional code snippet into it.
        /// </summary>
        /// <param name="initialSort">
        /// Initial sorting will reduce variations in the generated application source
        /// that are created by different macro evaluation order on each deployment.
        /// </param>
        public void SortReferencesBeforeUsingConcept(InitialConceptsSort initialSort)
        {
            var sw = Stopwatch.StartNew();

            // Initial sort:

            if (initialSort != InitialConceptsSort.None)
            {
                var sortComparison = new Dictionary <InitialConceptsSort, Comparison <IConceptInfo> >
                {
                    { InitialConceptsSort.Key, (a, b) => a.GetKey().CompareTo(b.GetKey()) },
                    { InitialConceptsSort.KeyDescending, (a, b) => - a.GetKey().CompareTo(b.GetKey()) },
                };

                _resolvedConcepts.Sort(sortComparison[initialSort]);
                _performanceLogger.Write(sw, $"SortReferencesBeforeUsingConcept: Initial sort by {initialSort}.");
            }

            // Extract dependencies:

            var dependencies = new List <(IConceptInfo dependsOn, IConceptInfo dependent)>(_resolvedConcepts.Count);

            foreach (var concept in _resolvedConcepts)
            {
                foreach (ConceptMember member in ConceptMembers.Get(concept))
                {
                    if (member.IsConceptInfo)
                    {
                        dependencies.Add(((IConceptInfo)member.GetValue(concept), concept));
                    }
                }
            }

            var dependents                  = dependencies.ToMultiDictionary(d => d.dependsOn, d => d.dependent);
            var countOfDependencies         = dependencies.GroupBy(d => d.dependent).ToDictionary(group => group.Key, group => group.Count());
            var conceptsWithoutDependencies = _resolvedConcepts.Where(c => !countOfDependencies.ContainsKey(c)).ToList();

            conceptsWithoutDependencies = conceptsWithoutDependencies.Where(c => c is InitializationConcept)
                                          .Concat(conceptsWithoutDependencies.Where(c => !(c is InitializationConcept)))
                                          .ToList();
            var newWithoutDependencies = new List <IConceptInfo>(conceptsWithoutDependencies.Count / 2);

            // Sort by dependencies:

            var sortedList = new List <IConceptInfo>(_resolvedConcepts.Count);

            while (sortedList.Count < _resolvedConcepts.Count)
            {
                if (!conceptsWithoutDependencies.Any())
                {
                    var unresolvedConcepts = countOfDependencies.Where(cd => cd.Value > 0).Select(c => c.Key).ToList();
                    int reportConcepts     = 5;
                    throw new FrameworkException($"Circular dependency detected while sorting concepts." +
                                                 $" Unresolved {unresolvedConcepts.Count} concepts:" +
                                                 $" {string.Join(", ", unresolvedConcepts.Take(reportConcepts).Select(c => c.GetUserDescription()))}" +
                                                 (unresolvedConcepts.Count > reportConcepts ? ", ..." : "."));
                }

                // Using a top-down breadth-first sorting, instead of a recursion, to provide more stable sort.
                // For example, adding a filter that references an entity should not change the ordering of the entities.
                newWithoutDependencies.Clear();
                foreach (var concept in conceptsWithoutDependencies)
                {
                    sortedList.Add(concept);
                    foreach (var dependent in dependents.Get(concept))
                    {
                        int remaining = --countOfDependencies[dependent];
                        if (remaining == 0)
                        {
                            newWithoutDependencies.Add(dependent);
                        }
                    }
                }

                conceptsWithoutDependencies.Clear();
                conceptsWithoutDependencies.AddRange(newWithoutDependencies);
            }

            // Result:

            if (conceptsWithoutDependencies.Any())
            {
                throw new FrameworkException($"Unexpected internal state: Remaining {conceptsWithoutDependencies.Count} {nameof(conceptsWithoutDependencies)} to resolve.");
            }
            if (countOfDependencies.Any(c => c.Value != 0))
            {
                throw new FrameworkException($"Unexpected internal state: Remaining {nameof(countOfDependencies)} != 0.");
            }

            _resolvedConcepts.Clear();
            _resolvedConcepts.AddRange(sortedList);
            _performanceLogger.Write(sw, "SortReferencesBeforeUsingConcept.");
        }
コード例 #20
0
ファイル: GenericParser.cs プロジェクト: TinOroz/Rhetos
        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));
            }
        }