<commit_before>﻿using System;
using System.Collections.Generic;
using System.Linq;
using Orchard.ContentManagement.Handlers;
using Orchard.ContentManagement.MetaData;
using Orchard.DisplayManagement;
using Orchard.DisplayManagement.Shapes;
using Orchard.Logging;

namespace Orchard.ContentManagement.Drivers {
    public abstract class ContentFieldDriver<TField> : IContentFieldDriver where TField : ContentField, new() {
        protected virtual string Prefix { get { return ""; } }
        protected virtual string Zone { get { return "Content"; } }

        void IContentFieldDriver.GetContentItemMetadata(GetContentItemMetadataContext context) {
            Process(context.ContentItem, (part, field) => GetContentItemMetadata(part, field, context.Metadata), context.Logger);
        }

        DriverResult IContentFieldDriver.BuildDisplayShape(BuildDisplayContext context) {
            return Process(context.ContentItem, (part, field) => {
                DriverResult result = Display(part, field, context.DisplayType, context.New);
                
                if (result != null) {
                    result.ContentPart = part;
                    result.ContentField = field;
                }
                
                return result;
            }, context.Logger);
        }

        DriverResult IContentFieldDriver.BuildEditorShape(BuildEditorContext context) {
            return Process(context.ContentItem, (part, field) => {
                DriverResult result =  Editor(part, field, context.New);
                
                if (result != null) {
                    result.ContentPart = part;
                    result.ContentField = field;
                }
                
                return result;
            }, context.Logger);
        }

        DriverResult IContentFieldDriver.UpdateEditorShape(UpdateEditorContext context) {
            return Process(context.ContentItem, (part, field) => {
                // Checking if the editor needs to be updated (e.g. if any of the shapes were not hidden).
                DriverResult editor = Editor(part, field, context.New);
                IEnumerable<ContentShapeResult> contentShapeResults = editor.GetShapeResults();
                
                if (contentShapeResults.Any(contentShapeResult =>
                    contentShapeResult == null || contentShapeResult.WasDisplayed(context))) {
                    DriverResult result = Editor(part, field, context.Updater, context.New);

                    if (result != null) {
                        result.ContentPart = part;
                        result.ContentField = field;
                    }

                    return result;
                }

                return editor;
            }, context.Logger);
        }

        void IContentFieldDriver.Importing(ImportContentContext context) {
            Process(context.ContentItem, (part, field) => Importing(part, field, context), context.Logger);
        }

        void IContentFieldDriver.Imported(ImportContentContext context) {
            Process(context.ContentItem, (part, field) => Imported(part, field, context), context.Logger);
        }

        void IContentFieldDriver.ImportCompleted(ImportContentContext context) {
            Process(context.ContentItem, (part, field) => ImportCompleted(part, field, context), context.Logger);
        }

        void IContentFieldDriver.Exporting(ExportContentContext context) {
            Process(context.ContentItem, (part, field) => Exporting(part, field, context), context.Logger);
        }

        void IContentFieldDriver.Exported(ExportContentContext context) {
            Process(context.ContentItem, (part, field) => Exported(part, field, context), context.Logger);
        }

        void IContentFieldDriver.Cloning(CloneContentContext context) {
            ProcessClone(context.ContentItem, context.CloneContentItem, (part, originalField, cloneField) => Cloning(part, originalField, cloneField, context), context.Logger);
        }

        void IContentFieldDriver.Cloned(CloneContentContext context) {
            ProcessClone(context.ContentItem, context.CloneContentItem, (part, originalField, cloneField) => Cloned(part, originalField, cloneField, context), context.Logger);
        }

        void IContentFieldDriver.Describe(DescribeMembersContext context) {
            Describe(context);
        }

        void Process(ContentItem item, Action<ContentPart, TField> effort, ILogger logger) {
            var occurences = item.Parts.SelectMany(part => part.Fields.OfType<TField>().Select(field => new { part, field }));
            occurences.Invoke(pf => effort(pf.part, pf.field), logger);
        }

        void ProcessClone(ContentItem originalItem, ContentItem cloneItem, Action<ContentPart, TField, TField> effort, ILogger logger) {
            var occurences = originalItem.Parts.SelectMany(part => part.Fields.OfType<TField>().Select(field => new { part, field }))
                .Join(cloneItem.Parts.SelectMany(part => part.Fields.OfType<TField>()), original => original.field.Name, cloneField => cloneField.Name, (original, cloneField) => new { original, cloneField } );
            occurences.Invoke(pf => effort(pf.original.part, pf.original.field, pf.cloneField), logger);
        }

        DriverResult Process(ContentItem item, Func<ContentPart, TField, DriverResult> effort, ILogger logger) {
            var results = item.Parts
                .SelectMany(part => part.Fields.OfType<TField>().Select(field => new { part, field }))
                .Invoke(pf => effort(pf.part, pf.field), logger);

            return Combined(results.ToArray());
        }

        public IEnumerable<ContentFieldInfo> GetFieldInfo() {
            var contentFieldInfo = new[] {
                new ContentFieldInfo {
                    FieldTypeName = typeof (TField).Name,
                    Factory = (partFieldDefinition, storage) => new TField {
                        PartFieldDefinition = partFieldDefinition,
                        Storage = storage,
                    }
                }
            };

            return contentFieldInfo;
        }

        protected virtual void GetContentItemMetadata(ContentPart part, TField field, ContentItemMetadata metadata) { }

        protected virtual DriverResult Display(ContentPart part, TField field, string displayType, dynamic shapeHelper) { return null; }
        protected virtual DriverResult Editor(ContentPart part, TField field, dynamic shapeHelper) { return null; }
        protected virtual DriverResult Editor(ContentPart part, TField field, IUpdateModel updater, dynamic shapeHelper) { return null; }
        
        protected virtual void Importing(ContentPart part, TField field, ImportContentContext context) { }
        protected virtual void Imported(ContentPart part, TField field, ImportContentContext context) { }
        protected virtual void ImportCompleted(ContentPart part, TField field, ImportContentContext context) { }
        protected virtual void Exporting(ContentPart part, TField field, ExportContentContext context) { }
        protected virtual void Exported(ContentPart part, TField field, ExportContentContext context) { }
        protected virtual void Cloning(ContentPart part, TField originalField, TField cloneField, CloneContentContext context) { }
        protected virtual void Cloned(ContentPart part, TField originalField, TField cloneField, CloneContentContext context) { }

        protected virtual void Describe(DescribeMembersContext context) { }

        public ContentShapeResult ContentShape(string shapeType, Func<dynamic> factory) {
            return ContentShapeImplementation(shapeType, null, ctx => factory());
        }

        public ContentShapeResult ContentShape(string shapeType, string differentiator, Func<dynamic> factory) {
            return ContentShapeImplementation(shapeType, differentiator, ctx => factory());
        }

        public ContentShapeResult ContentShape(string shapeType, Func<dynamic, dynamic> factory) {
            return ContentShapeImplementation(shapeType, null, ctx => factory(CreateShape(ctx, shapeType)));
        }

        public ContentShapeResult ContentShape(string shapeType, string differentiator, Func<dynamic, dynamic> factory) {
            return ContentShapeImplementation(shapeType, differentiator, ctx => factory(CreateShape(ctx, shapeType)));
        }

        private ContentShapeResult ContentShapeImplementation(string shapeType, string differentiator, Func<BuildShapeContext, object> shapeBuilder) {
            return new ContentShapeResult(shapeType, Prefix, ctx => AddAlternates(shapeBuilder(ctx), ctx, differentiator)).Differentiator(differentiator);
        }

        private static object AddAlternates(dynamic shape, BuildShapeContext ctx, string differentiator) {
            // automatically add shape alternates for shapes added by fields
            // for fields on dynamic parts the part name is the same as the content type name

            ShapeMetadata metadata = shape.Metadata;
<commit_msg>[Fixes #7024] Added null check for the shape returned by a field driver<commit_after>﻿using System;
using System.Collections.Generic;
using System.Linq;
using Orchard.ContentManagement.Handlers;
using Orchard.ContentManagement.MetaData;
using Orchard.DisplayManagement;
using Orchard.DisplayManagement.Shapes;
using Orchard.Logging;

namespace Orchard.ContentManagement.Drivers {
    public abstract class ContentFieldDriver<TField> : IContentFieldDriver where TField : ContentField, new() {
        protected virtual string Prefix { get { return ""; } }
        protected virtual string Zone { get { return "Content"; } }

        void IContentFieldDriver.GetContentItemMetadata(GetContentItemMetadataContext context) {
            Process(context.ContentItem, (part, field) => GetContentItemMetadata(part, field, context.Metadata), context.Logger);
        }

        DriverResult IContentFieldDriver.BuildDisplayShape(BuildDisplayContext context) {
            return Process(context.ContentItem, (part, field) => {
                DriverResult result = Display(part, field, context.DisplayType, context.New);
                
                if (result != null) {
                    result.ContentPart = part;
                    result.ContentField = field;
                }
                
                return result;
            }, context.Logger);
        }

        DriverResult IContentFieldDriver.BuildEditorShape(BuildEditorContext context) {
            return Process(context.ContentItem, (part, field) => {
                DriverResult result =  Editor(part, field, context.New);
                
                if (result != null) {
                    result.ContentPart = part;
                    result.ContentField = field;
                }
                
                return result;
            }, context.Logger);
        }

        DriverResult IContentFieldDriver.UpdateEditorShape(UpdateEditorContext context) {
            return Process(context.ContentItem, (part, field) => {
                // Checking if the editor needs to be updated (e.g. if any of the shapes were not hidden).
                DriverResult editor = Editor(part, field, context.New);
                IEnumerable<ContentShapeResult> contentShapeResults = editor.GetShapeResults();
                
                if (contentShapeResults.Any(contentShapeResult =>
                    contentShapeResult == null || contentShapeResult.WasDisplayed(context))) {
                    DriverResult result = Editor(part, field, context.Updater, context.New);

                    if (result != null) {
                        result.ContentPart = part;
                        result.ContentField = field;
                    }

                    return result;
                }

                return editor;
            }, context.Logger);
        }

        void IContentFieldDriver.Importing(ImportContentContext context) {
            Process(context.ContentItem, (part, field) => Importing(part, field, context), context.Logger);
        }

        void IContentFieldDriver.Imported(ImportContentContext context) {
            Process(context.ContentItem, (part, field) => Imported(part, field, context), context.Logger);
        }

        void IContentFieldDriver.ImportCompleted(ImportContentContext context) {
            Process(context.ContentItem, (part, field) => ImportCompleted(part, field, context), context.Logger);
        }

        void IContentFieldDriver.Exporting(ExportContentContext context) {
            Process(context.ContentItem, (part, field) => Exporting(part, field, context), context.Logger);
        }

        void IContentFieldDriver.Exported(ExportContentContext context) {
            Process(context.ContentItem, (part, field) => Exported(part, field, context), context.Logger);
        }

        void IContentFieldDriver.Cloning(CloneContentContext context) {
            ProcessClone(context.ContentItem, context.CloneContentItem, (part, originalField, cloneField) => Cloning(part, originalField, cloneField, context), context.Logger);
        }

        void IContentFieldDriver.Cloned(CloneContentContext context) {
            ProcessClone(context.ContentItem, context.CloneContentItem, (part, originalField, cloneField) => Cloned(part, originalField, cloneField, context), context.Logger);
        }

        void IContentFieldDriver.Describe(DescribeMembersContext context) {
            Describe(context);
        }

        void Process(ContentItem item, Action<ContentPart, TField> effort, ILogger logger) {
            var occurences = item.Parts.SelectMany(part => part.Fields.OfType<TField>().Select(field => new { part, field }));
            occurences.Invoke(pf => effort(pf.part, pf.field), logger);
        }

        void ProcessClone(ContentItem originalItem, ContentItem cloneItem, Action<ContentPart, TField, TField> effort, ILogger logger) {
            var occurences = originalItem.Parts.SelectMany(part => part.Fields.OfType<TField>().Select(field => new { part, field }))
                .Join(cloneItem.Parts.SelectMany(part => part.Fields.OfType<TField>()), original => original.field.Name, cloneField => cloneField.Name, (original, cloneField) => new { original, cloneField } );
            occurences.Invoke(pf => effort(pf.original.part, pf.original.field, pf.cloneField), logger);
        }

        DriverResult Process(ContentItem item, Func<ContentPart, TField, DriverResult> effort, ILogger logger) {
            var results = item.Parts
                .SelectMany(part => part.Fields.OfType<TField>().Select(field => new { part, field }))
                .Invoke(pf => effort(pf.part, pf.field), logger);

            return Combined(results.ToArray());
        }

        public IEnumerable<ContentFieldInfo> GetFieldInfo() {
            var contentFieldInfo = new[] {
                new ContentFieldInfo {
                    FieldTypeName = typeof (TField).Name,
                    Factory = (partFieldDefinition, storage) => new TField {
                        PartFieldDefinition = partFieldDefinition,
                        Storage = storage,
                    }
                }
            };

            return contentFieldInfo;
        }

        protected virtual void GetContentItemMetadata(ContentPart part, TField field, ContentItemMetadata metadata) { }

        protected virtual DriverResult Display(ContentPart part, TField field, string displayType, dynamic shapeHelper) { return null; }
        protected virtual DriverResult Editor(ContentPart part, TField field, dynamic shapeHelper) { return null; }
        protected virtual DriverResult Editor(ContentPart part, TField field, IUpdateModel updater, dynamic shapeHelper) { return null; }
        
        protected virtual void Importing(ContentPart part, TField field, ImportContentContext context) { }
        protected virtual void Imported(ContentPart part, TField field, ImportContentContext context) { }
        protected virtual void ImportCompleted(ContentPart part, TField field, ImportContentContext context) { }
        protected virtual void Exporting(ContentPart part, TField field, ExportContentContext context) { }
        protected virtual void Exported(ContentPart part, TField field, ExportContentContext context) { }
        protected virtual void Cloning(ContentPart part, TField originalField, TField cloneField, CloneContentContext context) { }
        protected virtual void Cloned(ContentPart part, TField originalField, TField cloneField, CloneContentContext context) { }

        protected virtual void Describe(DescribeMembersContext context) { }

        public ContentShapeResult ContentShape(string shapeType, Func<dynamic> factory) {
            return ContentShapeImplementation(shapeType, null, ctx => factory());
        }

        public ContentShapeResult ContentShape(string shapeType, string differentiator, Func<dynamic> factory) {
            return ContentShapeImplementation(shapeType, differentiator, ctx => factory());
        }

        public ContentShapeResult ContentShape(string shapeType, Func<dynamic, dynamic> factory) {
            return ContentShapeImplementation(shapeType, null, ctx => factory(CreateShape(ctx, shapeType)));
        }

        public ContentShapeResult ContentShape(string shapeType, string differentiator, Func<dynamic, dynamic> factory) {
            return ContentShapeImplementation(shapeType, differentiator, ctx => factory(CreateShape(ctx, shapeType)));
        }

        private ContentShapeResult ContentShapeImplementation(string shapeType, string differentiator, Func<BuildShapeContext, object> shapeBuilder) {
            var result = new ContentShapeResult(shapeType, Prefix, ctx => {
                var shape = shapeBuilder(ctx);

                if (shape == null) {
                    return null;
                }

                return AddAlternates(shape, ctx, differentiator);
            });

            if (result == null) {
                return null;
            }

            return result.Differentiator(differentiator);
        }

        private static object AddAlternates(dynamic shape, BuildShapeContext ctx, string differentiator) {
            // automatically add shape alternates for shapes added by fields
            // for fields on dynamic parts the part name is the same as the content type name

            ShapeMetadata metadata = shape.Metadata;

{'fraction_non_alphanumeric': 0.08603309794620236, 'fraction_numerical': 0.0002156217993639157, 'mean_word_length': 2.651968503937008, 'pattern_counts': {'":': 0, '<': 33, '<?xml version=': 0, '>': 95, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1562144', 'n_tokens_mistral': 4440, 'n_tokens_neox': 4275, 'n_words': 1133}