public ShapeTable GetShapeTable(string themeName)
        {
            return(_cacheManager.Get(themeName ?? "", x => {
                Logger.Information("Start building shape table");

                var alterationSets = _parallelCacheContext.RunInParallel(_bindingStrategies, bindingStrategy => {
                    Feature strategyDefaultFeature = bindingStrategy.Metadata.ContainsKey("Feature") ?
                                                     (Feature)bindingStrategy.Metadata["Feature"] :
                                                     null;

                    var builder = new ShapeTableBuilder(strategyDefaultFeature);
                    bindingStrategy.Value.Discover(builder);
                    return builder.BuildAlterations().ToReadOnlyCollection();
                });

                var alterations = alterationSets
                                  .SelectMany(shapeAlterations => shapeAlterations)
                                  .Where(alteration => IsModuleOrRequestedTheme(alteration, themeName))
                                  .OrderByDependenciesAndPriorities(AlterationHasDependency, GetPriority)
                                  .ToList();

                var descriptors = alterations.GroupBy(alteration => alteration.ShapeType, StringComparer.OrdinalIgnoreCase)
                                  .Select(group => group.Aggregate(
                                              new ShapeDescriptor {
                    ShapeType = group.Key
                },
                                              (descriptor, alteration) => {
                    alteration.Alter(descriptor);
                    return descriptor;
                })).ToList();

                foreach (var descriptor in descriptors)
                {
                    foreach (var alteration in alterations.Where(a => a.ShapeType == descriptor.ShapeType).ToList())
                    {
                        var local = new ShapeDescriptor {
                            ShapeType = descriptor.ShapeType
                        };
                        alteration.Alter(local);
                        descriptor.BindingSources.Add(local.BindingSource);
                    }
                }

                var result = new ShapeTable {
                    Descriptors = descriptors.ToDictionary(sd => sd.ShapeType, StringComparer.OrdinalIgnoreCase),
                    Bindings = descriptors.SelectMany(sd => sd.Bindings).ToDictionary(kv => kv.Key, kv => kv.Value, StringComparer.OrdinalIgnoreCase),
                };

                _shapeTableEventHandlers.Invoke(ctx => ctx.ShapeTableCreated(result), Logger);

                _shapeTableMonitors.Invoke(ctx => ctx.Monitor(x.Monitor), Logger);

                Logger.Information("Done building shape table");
                return result;
            }));
        }
        public ShapeTable GetShapeTable(string themeName)
        {
            return _cacheManager.Get(themeName ?? "", x => {
                Logger.Information("Start building shape table");

                var alterationSets = _parallelCacheContext.RunInParallel(_bindingStrategies, bindingStrategy => {
                    Feature strategyDefaultFeature = bindingStrategy.Metadata.ContainsKey("Feature") ?
                                                               (Feature)bindingStrategy.Metadata["Feature"] :
                                                               null;

                    var builder = new ShapeTableBuilder(strategyDefaultFeature);
                    bindingStrategy.Value.Discover(builder);
                    return builder.BuildAlterations().ToReadOnlyCollection();
                });

                var alterations = alterationSets
                .SelectMany(shapeAlterations => shapeAlterations)
                .Where(alteration => IsModuleOrRequestedTheme(alteration, themeName))
                .OrderByDependenciesAndPriorities(AlterationHasDependency, GetPriority)
                .ToList();

                var descriptors = alterations.GroupBy(alteration => alteration.ShapeType, StringComparer.OrdinalIgnoreCase)
                    .Select(group => group.Aggregate(
                        new ShapeDescriptor { ShapeType = group.Key },
                        (descriptor, alteration) => {
                            alteration.Alter(descriptor);
                            return descriptor;
                        })).ToList();

                foreach(var descriptor in descriptors) {
                    foreach(var alteration in alterations.Where(a => a.ShapeType == descriptor.ShapeType).ToList()) {
                        var local = new ShapeDescriptor { ShapeType = descriptor.ShapeType };
                        alteration.Alter(local);
                        descriptor.BindingSources.Add(local.BindingSource);
                    }
                }

                var result = new ShapeTable {
                    Descriptors = descriptors.ToDictionary(sd => sd.ShapeType, StringComparer.OrdinalIgnoreCase),
                    Bindings = descriptors.SelectMany(sd => sd.Bindings).ToDictionary(kv => kv.Key, kv => kv.Value, StringComparer.OrdinalIgnoreCase),
                };

                _shapeTableEventHandlers.Invoke(ctx => ctx.ShapeTableCreated(result), Logger);

                _shapeTableMonitors.Invoke(ctx => ctx.Monitor(x.Monitor), Logger);

                Logger.Information("Done building shape table");
                return result;
            });
        }
 void IShapeTableProvider.Discover(ShapeTableBuilder builder)
 {
     foreach (var pair in FeatureShapes) {
         foreach (var shape in pair.Value) {
             builder.Describe(shape).From(pair.Key).BoundAs(pair.Key.Descriptor.Id, null);
         }
     }
     Discover(builder);
 }
        public void Discover(ShapeTableBuilder builder)
        {
            // the root page shape named 'Layout' is wrapped with 'Document'
            // and has an automatic zone creating behavior
            builder.Describe("Layout")
                .Configure(descriptor => descriptor.Wrappers.Add("Document"))
                .OnCreating(creating => creating.Create = () => new ZoneHolding(() => creating.New.Zone()))
                .OnCreated(created => {
                    var layout = created.Shape;

                    layout.Head = created.New.DocumentZone(ZoneName: "Head");
                    layout.Body = created.New.DocumentZone(ZoneName: "Body");
                    layout.Tail = created.New.DocumentZone(ZoneName: "Tail");

                    layout.Body.Add(created.New.PlaceChildContent(Source: layout));

                    layout.Content = created.New.Zone();
                    layout.Content.ZoneName = "Content";
                    layout.Content.Add(created.New.PlaceChildContent(Source: layout));

                });

            // 'Zone' shapes are built on the Zone base class
            // They have class="zone zone-{name}"
            // and the template can be specialized with "Zone-{Name}" base file name
            builder.Describe("Zone")
                .OnCreating(creating => creating.Create = () => new Zone())
                .OnDisplaying(displaying => {
                    var zone = displaying.Shape;
                    string zoneName = zone.ZoneName;
                    zone.Classes.Add("zone-" + zoneName.HtmlClassify());
                    zone.Classes.Add("zone");

                    // Zone__[ZoneName] e.g. Zone-SideBar
                    zone.Metadata.Alternates.Add("Zone__" + zoneName);
                });

            builder.Describe("Menu")
                .OnDisplaying(displaying => {
                    var menu = displaying.Shape;
                    string menuName = menu.MenuName;
                    menu.Classes.Add("menu-" + menuName.HtmlClassify());
                    menu.Classes.Add("menu");
                    menu.Metadata.Alternates.Add("Menu__" + EncodeAlternateElement(menuName));
                });

            builder.Describe("MenuItem")
                .OnDisplaying(displaying => {
                    var menuItem = displaying.Shape;
                    var menu = menuItem.Menu;
                    menuItem.Metadata.Alternates.Add("MenuItem__" + EncodeAlternateElement(menu.MenuName));
                });

            builder.Describe("MenuItemLink")
                .OnDisplaying(displaying => {
                    var menuItem = displaying.Shape;
                    string menuName = menuItem.Menu.MenuName;
                    string contentType = null;
                    if (menuItem.Content != null) {
                        contentType = ((IContent) menuItem.Content).ContentItem.ContentType;
                    }

                    // MenuItemLink__[ContentType] e.g. MenuItemLink-HtmlMenuItem
                    if (contentType != null) {
                        menuItem.Metadata.Alternates.Add("MenuItemLink__" + EncodeAlternateElement(contentType));
                    }

                    // MenuItemLink__[MenuName] e.g. MenuItemLink-Main-Menu
                    menuItem.Metadata.Alternates.Add("MenuItemLink__" + EncodeAlternateElement(menuName));

                    // MenuItemLink__[MenuName]__[ContentType] e.g. MenuItemLink-Main-Menu-HtmlMenuItem
                    if (contentType != null) {
                        menuItem.Metadata.Alternates.Add("MenuItemLink__" + EncodeAlternateElement(menuName) + "__" + EncodeAlternateElement(contentType));
                    }

                });

            builder.Describe("LocalMenu")
                .OnDisplaying(displaying => {
                    var menu = displaying.Shape;
                    string menuName = menu.MenuName;
                    menu.Classes.Add("localmenu-" + menuName.HtmlClassify());
                    menu.Classes.Add("localmenu");
                    menu.Metadata.Alternates.Add("LocalMenu__" + EncodeAlternateElement(menuName));
                });

            builder.Describe("LocalMenuItem")
                .OnDisplaying(displaying => {
                    var menuItem = displaying.Shape;
                    var menu = menuItem.Menu;
                    menuItem.Metadata.Alternates.Add("LocalMenuItem__" + EncodeAlternateElement(menu.MenuName));
                });

            #region Pager alternates
            builder.Describe("Pager")
                .OnDisplaying(displaying => {
                    var pager = displaying.Shape;
                    string pagerId = pager.PagerId;
                    if (!String.IsNullOrWhiteSpace(pagerId))
                        displaying.Shape.Metadata.Alternates.Add("Pager__" + EncodeAlternateElement(pagerId));
                });

            builder.Describe("Pager_Gap")
                .OnDisplaying(displaying => {
                    var pager = displaying.Shape.Pager;
                    string pagerId = pager.PagerId;
                    if (!String.IsNullOrWhiteSpace(pagerId))
                        pager.Metadata.Alternates.Add("Pager_Gap__" + EncodeAlternateElement(pagerId));
                });

            builder.Describe("Pager_First")
                .OnDisplaying(displaying => {
                    var pager = displaying.Shape.Pager;
                    string pagerId = pager.PagerId;
                    if (!String.IsNullOrWhiteSpace(pagerId))
                        displaying.Shape.Metadata.Alternates.Add("Pager_First__" + EncodeAlternateElement(pagerId));
                });

            builder.Describe("Pager_Previous")
                .OnDisplaying(displaying => {
                    var pager = displaying.Shape.Pager;
                    string pagerId = pager.PagerId;
                    if (!String.IsNullOrWhiteSpace(pagerId))
                        displaying.Shape.Metadata.Alternates.Add("Pager_Previous__" + EncodeAlternateElement(pagerId));
                });

            builder.Describe("Pager_Next")
                .OnDisplaying(displaying => {
                    var pager = displaying.Shape.Pager;
                    string pagerId = pager.PagerId;
                    if (!String.IsNullOrWhiteSpace(pagerId))
                        displaying.Shape.Metadata.Alternates.Add("Pager_Next__" + EncodeAlternateElement(pagerId));
                });

            builder.Describe("Pager_Last")
                .OnDisplaying(displaying => {
                    var pager = displaying.Shape.Pager;
                    string pagerId = pager.PagerId;
                    if (!String.IsNullOrWhiteSpace(pagerId))
                        displaying.Shape.Metadata.Alternates.Add("Pager_Last__" + EncodeAlternateElement(pagerId));
                });

            builder.Describe("Pager_CurrentPage")
                .OnDisplaying(displaying => {
                    var pager = displaying.Shape.Pager;
                    string pagerId = pager.PagerId;
                    if (!String.IsNullOrWhiteSpace(pagerId))
                        displaying.Shape.Metadata.Alternates.Add("Pager_CurrentPage__" + EncodeAlternateElement(pagerId));
                });

            builder.Describe("Pager_Links")
                .OnDisplaying(displaying => {
                    var pager = displaying.Shape;
                    string pagerId = pager.PagerId;
                    if (!String.IsNullOrWhiteSpace(pagerId))
                        displaying.Shape.Metadata.Alternates.Add("Pager_Links__" + EncodeAlternateElement(pagerId));
                });

            #endregion

            // 'List' shapes start with several empty collections
            builder.Describe("List")
                .OnCreated(created => {
                    var list = created.Shape;
                    list.Tag = "ul";
                    list.ItemClasses = new List<string>();
                    list.ItemAttributes = new Dictionary<string, string>();
                });

            builder.Describe("Style")
                .OnDisplaying(displaying => {
                    var resource = displaying.Shape;
                    string url = resource.Url;
                    string fileName = StaticFileBindingStrategy.GetAlternateShapeNameFromFileName(url);
                    if (!string.IsNullOrEmpty(fileName)) {
                        resource.Metadata.Alternates.Add("Style__" + fileName);
                    }
                });

            builder.Describe("Script")
                .OnDisplaying(displaying => {
                    var resource = displaying.Shape;
                    string url = resource.Url;
                    string fileName = StaticFileBindingStrategy.GetAlternateShapeNameFromFileName(url);
                    if (!string.IsNullOrEmpty(fileName)) {
                        resource.Metadata.Alternates.Add("Script__" + fileName);
                    }
                });

            builder.Describe("Resource")
                .OnDisplaying(displaying => {
                    var resource = displaying.Shape;
                    string url = resource.Url;
                    string fileName = StaticFileBindingStrategy.GetAlternateShapeNameFromFileName(url);
                    if (!string.IsNullOrEmpty(fileName)) {
                        resource.Metadata.Alternates.Add("Resource__" + fileName);
                    }
                });
        }
 static IEnumerable<ShapeAlteration> GetAlterationBuilders(IShapeTableProvider strategy)
 {
     var builder = new ShapeTableBuilder(null);
     strategy.Discover(builder);
     return builder.BuildAlterations();
 }