Exemplo n.º 1
0
        private static RenderingBlock GetRenderingFromTemp(TempBlock b)
        {
            var renderingBlocks = b.Blocks.Select(GetRenderingFromTemp); // recurse

            return(new RenderingBlock(b.Name, b.Source, renderingBlocks, b.Data, b.Fragment, b.Cache));
        }
Exemplo n.º 2
0
        // input is a collection of block data values
        //   defined at structure level or under a named blocks chain
        // output is the resulting collection of temp blocks
        //
        // named blocks are collapsed into one temp block each
        // anonymous blocks each match to one temp block
        //
        // blockDataValues is ordered bottom-top
        // returns blocks ordered top-bottom
        private static IEnumerable <TempBlock> GetTempFromData(IEnumerable <WithLevel <BlockDataValue> > blockDataValues)
        {
            var blockDataValuesArray = blockDataValues.ToArray(); // iterate only once

            // find named blocks
            // the name is case-insensitive
            var namedBlockDataValueDictionary = new Dictionary <string, List <WithLevel <BlockDataValue> > >(StringComparer.InvariantCultureIgnoreCase);

            foreach (var b in blockDataValuesArray.Where(x => x.Item.IsNamed))
            {
                List <WithLevel <BlockDataValue> > namedBlockDataValues;
                if (!namedBlockDataValueDictionary.TryGetValue(b.Item.Name, out namedBlockDataValues))
                {
                    namedBlockDataValues = namedBlockDataValueDictionary[b.Item.Name] = new List <WithLevel <BlockDataValue> >();
                }
                namedBlockDataValues.Add(b);
            }
            foreach (var kvp in namedBlockDataValueDictionary)
            {
                kvp.Value.Reverse(); // top-to-bottom
            }
            // create the temporary named blocks from the top-most block data value
            // whether bottom named blocks can change any of these settings is an option
            var namedTempBlocks = new Dictionary <string, TempBlock>(StringComparer.InvariantCultureIgnoreCase);

            foreach (var namedBlockDataValues in namedBlockDataValueDictionary.Values)
            {
                var blockDataValue = namedBlockDataValues[0].Item;
                var blockLevel     = namedBlockDataValues[0].Level;

                var t = new TempBlock
                {
                    Name     = blockDataValue.Name,
                    Source   = blockDataValue.Source,
                    Index    = blockDataValue.Index,
                    Fragment = blockDataValue.Fragment,
                    Cache    = blockDataValue.Cache
                };
                t.MergeData(blockDataValue.Data);
                namedTempBlocks[blockDataValue.Name] = t;

                t.BlockDataValues.AddRange(blockDataValue.Blocks
                                           .Where(b => b.MinLevel <= blockLevel && b.MaxLevel >= blockLevel)
                                           .Select(x => new WithLevel <BlockDataValue>(x, blockLevel)));

                foreach (var namedOtherDataValue in namedBlockDataValues.Skip(1)) // top-bottom
                {
                    var otherDataValue = namedOtherDataValue.Item;
                    var otherLevel     = namedOtherDataValue.Level;

                    if (otherDataValue.IsKill)
                    {
                        t.Killed = true;
                        break; // no need to continue
                    }

                    // set source if not already set
                    if (!string.IsNullOrWhiteSpace(otherDataValue.Source))
                    {
                        if (!string.IsNullOrWhiteSpace(t.Source))
                        {
                            throw new StructureException("Cannot change source of a named blocks once it has been set.");
                        }
                        t.Source = otherDataValue.Source;
                    }

                    // index cannot change
                    if (otherDataValue.Index != BlockDataValue.DefaultIndex)
                    {
                        throw new StructureException("Only the top-most named block can define an index.");
                    }

                    // merge data
                    t.MergeData(otherDataValue.Data);

                    // override fragment
                    if (otherDataValue.Fragment != null)
                    {
                        t.Fragment = otherDataValue.Fragment;
                    }

                    // set cache if not already set
                    if (otherDataValue.Cache != null)
                    {
                        if (t.Cache != null)
                        {
                            throw new StructureException("Cannot change cache of a named blocks once it has been set.");
                        }
                        t.Cache = otherDataValue.Cache;
                    }

                    if (otherDataValue.IsReset)
                    {
                        t.BlockDataValues.Clear();
                    }

                    t.BlockDataValues.AddRange(otherDataValue.Blocks
                                               .Where(b => b.MinLevel <= otherLevel && b.MaxLevel >= otherLevel)
                                               .Select(x => new WithLevel <BlockDataValue>(x, otherLevel)));
                }

                t.BlockDataValues.Reverse();
            }

            // build the temporary blocks list
            var tempBlocks = new List <TempBlock>();

            foreach (var block in blockDataValuesArray) // bottom-top
            {
                var blockDataValue = block.Item;
                var blockLevel     = block.Level;

                // check if it matches a named block
                TempBlock namedTempBlock;
                if (namedTempBlocks.TryGetValue(blockDataValue.Name, out namedTempBlock))
                {
                    // named block
                    // insert temp block at the position of the top-most occurence
                    var isTopMost = block == namedBlockDataValueDictionary[blockDataValue.Name][0];
                    if (isTopMost && !namedTempBlock.Killed)
                    {
                        tempBlocks.Add(namedTempBlock);
                    }
                }
                else
                {
                    // not a named block

                    // just add a new temp block
                    var t = new TempBlock
                    {
                        Name     = string.Empty, // not a named block
                        Source   = blockDataValue.Source,
                        Index    = blockDataValue.Index,
                        Fragment = blockDataValue.Fragment,
                        Cache    = blockDataValue.Cache,

                        // there's nothing to merge so all blocks are defined at the same level
                        // block.Item.Blocks is top-bottom, must reverse
                        Blocks = GetTempFromData(block.Item.Blocks.Reverse()
                                                 .Where(b => b.MinLevel <= blockLevel && b.MaxLevel >= blockLevel)
                                                 .Select(b => new WithLevel <BlockDataValue>(b, blockLevel)))
                    };
                    t.MergeData(blockDataValue.Data);
                    tempBlocks.Add(t);
                }
            }

            // process named blocks source & inner blocks
            foreach (var tempBlock in namedTempBlocks.Values)
            {
                // for a named block, source may be missing and then we use the name as a source
                tempBlock.Source = string.IsNullOrWhiteSpace(tempBlock.Source) ? tempBlock.Name : tempBlock.Source;

                // tempBlock is a named block so tempBlock.BlockDatas is already bottom-top ordered
                tempBlock.Blocks = GetTempFromData(tempBlock.BlockDataValues); // recurse
            }

            tempBlocks.Reverse(); // return top-bottom

            // sort according to indexes
            // beware! List<T>.Sort() is documented as performing an unstable sort
            // whereas Enumerable.OrderBy<TSource, TKey>.Sort() is documented as performing a stable sort,
            // a stable sort meaning that when indexes are equals, the existing order is preserved
            //tempBlocks.Sort((b1, b2) => b1.Index - b2.Index); // NOT!
            tempBlocks = tempBlocks.OrderBy(x => x.Index).ToList();
            return(tempBlocks);
        }