/// <summary>
        ///     Checks for all elements with a based-on field that the referenced ids exist and have the
        ///     same type
        /// </summary>
        /// <exception cref="LoadException">
        ///     if base id does not exist or base element does not have the same type
        /// </exception>
        private void ValidateBaseIdsExist()
        {
            var errors = (
                from element in this._tles
                where element.BasedOnId != null &&
                this._tles.All(elem => elem.Id != element.BasedOnId)
                select element
                ).ToList();

            if (errors.Any())
            {
                throw LoadException.BaseElementNotFound(errors);
            }

            var typeErrors = (
                from element in this._tles
                where element.BasedOnId != null
                let baseElem = this._tles.First(e => e.Id == element.BasedOnId)
                               where !baseElem.GetType().IsInstanceOfType(element)
                               select(
                    Base: baseElem,
                    Target: element
                    )
                ).ToList();

            if (typeErrors.Any())
            {
                throw LoadException.BaseElementHasDifferentType(typeErrors);
            }
        }
        /// <summary>
        ///     process 'based-on' fields
        /// </summary>
        /// <exception cref="LoadException"></exception>
        private void RealizeInheritance()
        {
            var realisationQueue = new Queue <BasicElement>(this._tles);
            int stepsBeforeAbort = realisationQueue.Count;

            while (realisationQueue.Count > 0 && stepsBeforeAbort > 0)
            {
                stepsBeforeAbort--;
                bool processedElement = false;
                var  targetElem       = realisationQueue.Dequeue();

                if (targetElem.BasedOnId == null)
                {
                    processedElement = true;
                }
                else
                {
                    var baseElem = this._tles.First(e => e.Id == targetElem.BasedOnId);

                    // if base element is not done yet, postpone target element
                    if (realisationQueue.Contains(baseElem))
                    {
                        realisationQueue.Enqueue(targetElem);
                    }
                    else
                    {
                        // base element done, do the actual work
                        // properties contains all properties of baseElem type that have the YamlMember attribute
                        var properties =
                            from prop in baseElem.GetType().GetProperties()
                            where prop.IsDefined(typeof(YamlMemberAttribute), true)
                            select prop;

                        // for each of those properties
                        foreach (var prop in properties)
                        {
                            // set the target value to the base value if target value is not set
                            var targetValue = prop.GetValue(targetElem);
                            if (targetValue == null)
                            {
                                var baseValue = prop.GetValue(baseElem);
                                prop.SetValue(targetElem, baseValue);
                            }
                        }

                        processedElement = true;
                    }
                }

                if (processedElement)
                {
                    stepsBeforeAbort = realisationQueue.Count;
                }
            }

            if (realisationQueue.Count > 0)
            {
                throw LoadException.InheritanceLoopAborted(realisationQueue);
            }
        }
        /// <summary>
        ///     checks for duplicate ids and whether all ids are well-formed
        /// </summary>
        /// <exception cref="LoadException">if one or more elements does not fufill these rules</exception>
        private void ValidateUniqueWellformedIds()
        {
            var duplicates = (
                from tle in this._tles
                group tle by tle.Id
                into grouped
                where grouped.Count() > 1
                select(
                    grouped.Key,
                    grouped.AsEnumerable()
                    )
                ).ToList();

            if (duplicates.Any())
            {
                string msg = "The project contains duplicate element ids, which is not allowed.\n";
                foreach ((string id, var elements) in duplicates)
                {
                    msg += $"- id '{id}', defined in:\n";
                    msg  = elements.Aggregate(msg, (current, elem)
                                              => current + $"  - '{elem.OriginalFilePath}'\n");
                }

                throw new LoadException(msg);
            }

            // matches 'id', 'some-id', 'id-9-test', but not ' id ', '%KHGSI'
            var idRegex    = new Regex("[a-z][a-z]+(-([a-z]|[0-9])+)*"); // good regex tool: regexr.com
            var mismatches = this._tles.Where(tle => !idRegex.IsMatch(tle.Id)).ToList();

            if (mismatches.Any())
            {
                throw LoadException.MalformedId(mismatches);
            }
        }
        /// <summary>
        ///     Checks if all properties with the [YamlProperties] attribute and Required set to true
        ///     have a value for all of the loaded elements
        /// </summary>
        /// <exception cref="LoadException">if a required property is null</exception>
        private void ValidateRequiredFields()
        {
            var errors = (
                from element in this._tles
                from property in element.GetType().GetProperties()
                // ignore properties that have [YamlIgnore]
                where !property.IsDefined(typeof(YamlIgnoreAttribute)) &&
                property.GetValue(element) == null &&
                property.IsDefined(typeof(YamlPropertiesAttribute), true)
                let yamlPropsAttr = property.GetCustomAttribute <YamlPropertiesAttribute>()
                                    where yamlPropsAttr.Required
                                    let yamlMember = property.GetCustomAttribute <YamlMemberAttribute>()
                                                     select(
                    Element: element,
                    PropYamlName: yamlMember.Alias,
                    PropCsName: property.Name
                    )
                ).ToList();

            if (errors.Any())
            {
                throw LoadException.RequiredPropertyNull(errors);
            }
        }