protected override void ProcessFieldProperties(SPField field, FieldDefinition fieldModel)
        {
            // let base setting be setup
            base.ProcessFieldProperties(field, fieldModel);

            var typedField = field as SPFieldUser;
            var typedFieldModel = fieldModel.WithAssertAndCast<UserFieldDefinition>("model", value => value.RequireNotNull());

            typedField.AllowDisplay = typedFieldModel.AllowDisplay;
            typedField.Presence = typedFieldModel.Presence;
            typedField.AllowMultipleValues = typedFieldModel.AllowMultipleValues;

            if (!string.IsNullOrEmpty(typedFieldModel.SelectionMode))
                typedField.SelectionMode = (SPFieldUserSelectionMode)Enum.Parse(typeof(SPFieldUserSelectionMode), typedFieldModel.SelectionMode);

            if (typedFieldModel.SelectionGroup.HasValue)
            {
                typedField.SelectionGroup = typedFieldModel.SelectionGroup.Value;
            }
            else if (!string.IsNullOrEmpty(typedFieldModel.SelectionGroupName))
            {
                var group = GetCurrentWeb().SiteGroups.OfType<SPGroup>().FirstOrDefault(g => g.Name.ToUpper() == typedFieldModel.SelectionGroupName.ToUpper());
                typedField.SelectionGroup = group.ID;
            }
        }
        protected override void ProcessFieldProperties(Field field, FieldDefinition fieldModel)
        {
            var typedFieldModel = fieldModel.WithAssertAndCast<NoteFieldDefinition>("model", value => value.RequireNotNull());

            // the XML update goes first
            // then the rest of the normal props with base.ProcessFieldProperties(field, fieldModel);
            // then specific to NoteField props
            // as crazy as it sounds

            // RichTextMode  update
            // https://github.com/SubPointSolutions/spmeta2/issues/673
            if (!string.IsNullOrEmpty(typedFieldModel.RichTextMode))
            {
                var fieldXml = XDocument.Parse(field.SchemaXml);
                fieldXml.Root.SetAttribute("RichTextMode", typedFieldModel.RichTextMode);

                field.SchemaXml = fieldXml.ToString();
            }

            // let base setting be setup
            base.ProcessFieldProperties(field, fieldModel);

            var typedField = field.Context.CastTo<FieldMultiLineText>(field);

            typedField.NumberOfLines = typedFieldModel.NumberOfLines;
            typedField.AppendOnly = typedFieldModel.AppendOnly;
            typedField.RichText = typedFieldModel.RichText;
        }
        protected override void PostProcessFieldDefinitionInstance(FieldDefinition def, FieldReverseHost typedReverseHost, ReverseOptions options)
        {
            var context = typedReverseHost.HostClientContext;

            var typedField = context.CastTo<FieldLookup>(typedReverseHost.Field);
            var typedDef = def.WithAssertAndCast<LookupFieldDefinition>("modelHost", m => m.RequireNotNull());

            typedDef.AllowMultipleValues = typedField.AllowMultipleValues;

            if (typedDef.AllowMultipleValues)
                typedDef.FieldType = BuiltInFieldTypes.LookupMulti;
            else
                typedDef.FieldType = BuiltInFieldTypes.Lookup;

            //typedDef.AppendOnly = typedField.AppendOnly;
            //typedDef.RichText = typedField.RichText;

            //typedDef.NumberOfLines = typedField.NumberOfLines;

            //var xml = XDocument.Parse(typedField.SchemaXml);
            //var fieldXml = xml.Root;

            //var unlimValue = ConvertUtils.ToBool(fieldXml.GetAttributeValue("UnlimitedLengthInDocumentLibrary"));
            //typedDef.UnlimitedLengthInDocumentLibrary = unlimValue.HasValue ? unlimValue.Value : false;

            //var richTextMode = ConvertUtils.ToString(fieldXml.GetAttributeValue("RichTextMode"));
            //typedDef.RichTextMode = richTextMode;
        }
        protected override void ProcessFieldProperties(Field field, FieldDefinition fieldModel)
        {
            var site = HostSite;
            var context = site.Context;

            // let base setting be setup
            base.ProcessFieldProperties(field, fieldModel);

            var typedField = field.Context.CastTo<FieldLookup>(field);
            var typedFieldModel = fieldModel.WithAssertAndCast<DependentLookupFieldDefinition>("model", value => value.RequireNotNull());

            var primaryLookupField = GetPrimaryField(typedFieldModel);

            typedField.Context.Load(primaryLookupField);
            typedField.Context.ExecuteQueryWithTrace();

            if (string.IsNullOrEmpty(typedField.PrimaryFieldId))
            {
                typedField.PrimaryFieldId = primaryLookupField.Id.ToString();
            }
            typedField.ReadOnlyField = true;

            if (!string.IsNullOrEmpty(typedFieldModel.RelationshipDeleteBehavior))
            {
                var value = (RelationshipDeleteBehaviorType)Enum.Parse(typeof(RelationshipDeleteBehaviorType), typedFieldModel.RelationshipDeleteBehavior);
                typedField.RelationshipDeleteBehavior = value;
            }

            // unsupported in CSOM yet
            //dependentLookupField.UnlimitedLengthInDocumentLibrary = primaryLookupField.UnlimitedLengthInDocumentLibrary;
            typedField.Direction = primaryLookupField.Direction;
        }
        protected override void ProcessFieldProperties(SPField field, FieldDefinition fieldModel)
        {
            // let base setting be setup
            base.ProcessFieldProperties(field, fieldModel);

            if (!string.IsNullOrEmpty(fieldModel.ValidationMessage))
                field.ValidationMessage = fieldModel.ValidationMessage;

            if (!string.IsNullOrEmpty(fieldModel.ValidationFormula))
                field.ValidationFormula = fieldModel.ValidationFormula;


            var typedFieldModel = fieldModel.WithAssertAndCast<DateTimeFieldDefinition>("model", value => value.RequireNotNull());
            var typedField = field as SPFieldDateTime;

            if (!string.IsNullOrEmpty(typedFieldModel.CalendarType))
                typedField.CalendarType = (SPCalendarType)Enum.Parse(typeof(SPCalendarType), typedFieldModel.CalendarType);

            if (!string.IsNullOrEmpty(typedFieldModel.DisplayFormat))
                typedField.DisplayFormat = (SPDateTimeFieldFormatType)Enum.Parse(typeof(SPDateTimeFieldFormatType), typedFieldModel.DisplayFormat);

#if !NET35
            if (!string.IsNullOrEmpty(typedFieldModel.FriendlyDisplayFormat))
                typedField.FriendlyDisplayFormat = (SPDateTimeFieldFriendlyFormatType)Enum.Parse(typeof(SPDateTimeFieldFriendlyFormatType), typedFieldModel.FriendlyDisplayFormat);
#endif
        }
        protected override void CustomFieldTypeValidation(AssertPair<FieldDefinition, Field> assert, Field spObject, FieldDefinition definition)
        {
            var typedObject = spObject.Context.CastTo<FieldLookup>(spObject);
            var typedDefinition = definition.WithAssertAndCast<LookupFieldDefinition>("model", value => value.RequireNotNull());

            // https://github.com/SubPointSolutions/spmeta2/issues/310
            // AllowMultipleValues - TRUE - LookupMulti
            // AllowMultipleValues - FALSE - Lookup
            assert.ShouldBeEqual((p, s, d) =>
            {
                var srcProp = s.GetExpressionValue(m => m.FieldType);
                var dstProp = d.GetExpressionValue(m => d.TypeAsString);

                var isValid = typedDefinition.AllowMultipleValues
                    ? typedObject.TypeAsString == "LookupMulti"
                    : typedObject.TypeAsString == "Lookup";

                return new PropertyValidationResult
                {
                    Tag = p.Tag,
                    Src = srcProp,
                    Dst = dstProp,
                    IsValid = isValid
                };
            });
        }
        protected override void ProcessSPFieldXElement(XElement fieldTemplate, FieldDefinition fieldModel)
        {
            base.ProcessSPFieldXElement(fieldTemplate, fieldModel);

            var typedFieldModel = fieldModel.WithAssertAndCast<CurrencyFieldDefinition>("model", value => value.RequireNotNull());

            fieldTemplate.SetAttribute(BuiltInFieldAttributes.LCID, typedFieldModel.CurrencyLocaleId);
        }
        protected override void ProcessSPFieldXElement(XElement fieldTemplate, FieldDefinition fieldModel)
        {
            base.ProcessSPFieldXElement(fieldTemplate, fieldModel);

            var typedFieldModel = fieldModel.WithAssertAndCast<SummaryLinkFieldDefinition>("model", value => value.RequireNotNull());

            //fieldTemplate.SetAttribute(BuiltInFieldAttributes.Format, typedFieldModel.EditFormat);
        }
        protected override void ProcessFieldProperties(SPField field, FieldDefinition fieldModel)
        {
            // let base setting be setup
            base.ProcessFieldProperties(field, fieldModel);

            var typedFieldModel = fieldModel.WithAssertAndCast<SummaryLinkFieldDefinition>("model", value => value.RequireNotNull());
            var typedField = field as SummaryLinkField;
        }
        protected override void ProcessSPFieldXElement(XElement fieldTemplate, FieldDefinition fieldModel)
        {
            base.ProcessSPFieldXElement(fieldTemplate, fieldModel);

            var typedFieldModel = fieldModel.WithAssertAndCast<ComputedFieldDefinition>("model", value => value.RequireNotNull());

            if (typedFieldModel.EnableLookup.HasValue)
                fieldTemplate.SetAttribute(BuiltInFieldAttributes.EnableLookup, typedFieldModel.EnableLookup.ToString().ToUpper());
        }
        protected override void ProcessSPFieldXElement(XElement fieldTemplate, FieldDefinition fieldModel)
        {
            base.ProcessSPFieldXElement(fieldTemplate, fieldModel);

            var typedFieldModel = fieldModel.WithAssertAndCast<TextFieldDefinition>("model", value => value.RequireNotNull());

            if (typedFieldModel.MaxLength.HasValue)
                fieldTemplate.SetAttribute(BuiltInFieldAttributes.MaxLength, typedFieldModel.MaxLength.Value);
        }
        protected override void PostProcessFieldDefinitionInstance(FieldDefinition def, FieldReverseHost typedReverseHost, ReverseOptions options)
        {
            var context = typedReverseHost.HostClientContext;

            var typedField = context.CastTo<FieldText>(typedReverseHost.Field);
            var typedDef = def.WithAssertAndCast<TextFieldDefinition>("modelHost", m => m.RequireNotNull());

            typedDef.MaxLength = typedField.MaxLength;
        }
        protected override void ProcessSPFieldXElement(XElement fieldTemplate, FieldDefinition fieldModel)
        {
            base.ProcessSPFieldXElement(fieldTemplate, fieldModel);

            var typedFieldModel = fieldModel.WithAssertAndCast<ImageFieldDefinition>("model", value => value.RequireNotNull());

            // TODO
            //fieldTemplate.SetAttribute(BuiltInFieldAttributes.RichText, typedFieldModel.RichText.ToString().ToUpper());
        }
        protected override void ProcessSPFieldXElement(XElement fieldTemplate, FieldDefinition fieldModel)
        {
            base.ProcessSPFieldXElement(fieldTemplate, fieldModel);

            var typedFieldModel = fieldModel.WithAssertAndCast<URLFieldDefinition>("model", value => value.RequireNotNull());

            if (!string.IsNullOrEmpty(typedFieldModel.DisplayFormat))
                fieldTemplate.SetAttribute(BuiltInFieldAttributes.Format, typedFieldModel.DisplayFormat);
        }
        protected override void PostProcessFieldDefinitionInstance(FieldDefinition def, FieldReverseHost typedReverseHost, ReverseOptions options)
        {
            var context = typedReverseHost.HostClientContext;

            var typedField = context.CastTo<FieldCurrency>(typedReverseHost.Field);
            var typedDef = def.WithAssertAndCast<CurrencyFieldDefinition>("modelHost", m => m.RequireNotNull());

            if (typedField.CurrencyLocaleId > 0)
                typedDef.CurrencyLocaleId = typedField.CurrencyLocaleId;
        }
        protected override void ProcessFieldProperties(Field field, FieldDefinition fieldModel)
        {
            // let base setting be setup
            base.ProcessFieldProperties(field, fieldModel);

            var typedFieldModel = fieldModel.WithAssertAndCast<URLFieldDefinition>("model", value => value.RequireNotNull());
            var typedField = field.Context.CastTo<FieldUrl>(field);

            if (!string.IsNullOrEmpty(typedFieldModel.DisplayFormat))
                typedField.DisplayFormat = (UrlFieldFormatType)Enum.Parse(typeof(UrlFieldFormatType), typedFieldModel.DisplayFormat);
        }
        protected override void ProcessFieldProperties(SPField field, FieldDefinition fieldModel)
        {
            // let base setting be setup
            base.ProcessFieldProperties(field, fieldModel);

            var typedFieldModel = fieldModel.WithAssertAndCast<ImageFieldDefinition>("model", value => value.RequireNotNull());
            var typedField = field as ImageField;

            // TODO
            //typedField.NumberOfLines = typedFieldModel.NumberOfLines;
        }
        protected override void ProcessSPFieldXElement(XElement fieldTemplate, FieldDefinition fieldModel)
        {
            base.ProcessSPFieldXElement(fieldTemplate, fieldModel);

            var businessFieldModel = fieldModel.WithAssertAndCast<BusinessDataFieldDefinition>("model", value => value.RequireNotNull());

            fieldTemplate.SetAttribute(BuiltInFieldAttributes.SystemInstance, businessFieldModel.SystemInstanceName);
            fieldTemplate.SetAttribute(BuiltInFieldAttributes.EntityNamespace, businessFieldModel.EntityNamespace);
            fieldTemplate.SetAttribute(BuiltInFieldAttributes.EntityName, businessFieldModel.EntityName);
            fieldTemplate.SetAttribute(BuiltInFieldAttributes.BdcField, businessFieldModel.BdcFieldName);
        }
        protected override void ProcessFieldProperties(Field field, FieldDefinition fieldModel)
        {
            // let base setting be setup
            base.ProcessFieldProperties(field, fieldModel);

            var typedFieldModel = fieldModel.WithAssertAndCast<CalculatedFieldDefinition>("model", value => value.RequireNotNull());
            var typedField = field.Context.CastTo<FieldCalculated>(field);

            typedField.Formula = typedFieldModel.Formula ?? string.Empty;
            typedField.OutputType = typedField.OutputType = (FieldType)Enum.Parse(typeof(FieldType), typedFieldModel.OutputType);
        }
        protected override void ProcessFieldProperties(Field field, FieldDefinition fieldModel)
        {
            // let base setting be setup
            base.ProcessFieldProperties(field, fieldModel);

            var typedFieldModel = fieldModel.WithAssertAndCast<ComputedFieldDefinition>("model", value => value.RequireNotNull());
            var typedField = field.Context.CastTo<FieldComputed>(field);

            if (typedFieldModel.EnableLookup.HasValue)
                typedField.EnableLookup = typedFieldModel.EnableLookup.Value;
        }
        protected override void PostProcessFieldDefinitionInstance(FieldDefinition def, FieldReverseHost typedReverseHost, ReverseOptions options)
        {
            var context = typedReverseHost.HostClientContext;

            var typedField = context.CastTo<FieldDateTime>(typedReverseHost.Field);
            var typedDef = def.WithAssertAndCast<DateTimeFieldDefinition>("modelHost", m => m.RequireNotNull());

            typedDef.CalendarType = typedField.DateTimeCalendarType.ToString();
            typedDef.DisplayFormat = typedField.DisplayFormat.ToString();
            typedDef.FriendlyDisplayFormat = typedField.FriendlyDisplayFormat.ToString();
        }
        protected override void ProcessFieldProperties(SPField field, FieldDefinition fieldModel)
        {
            // let base setting be setup
            base.ProcessFieldProperties(field, fieldModel);

            var typedFieldModel = fieldModel.WithAssertAndCast<NoteFieldDefinition>("model", value => value.RequireNotNull());
            var typedField = field as SPFieldMultiLineText;

            typedField.NumberOfLines = typedFieldModel.NumberOfLines;
            typedField.AppendOnly = typedFieldModel.AppendOnly;
            typedField.RichText = typedFieldModel.RichText;
        }
        protected override void PostProcessFieldDefinitionInstance(FieldDefinition def, FieldReverseHost typedReverseHost, ReverseOptions options)
        {
            var context = typedReverseHost.HostClientContext;

            var typedField = context.CastTo<FieldUrl>(typedReverseHost.Field);
            var typedDef = def.WithAssertAndCast<URLFieldDefinition>("modelHost", m => m.RequireNotNull());

            var xml = XDocument.Parse(typedField.SchemaXml);
            var fieldXml = xml.Root;

            var displayFormat = ConvertUtils.ToString(fieldXml.GetAttributeValue(BuiltInFieldAttributes.Format));
            typedDef.DisplayFormat = displayFormat;
        }
        protected override void ProcessSPFieldXElement(XElement fieldTemplate, FieldDefinition fieldModel)
        {
            base.ProcessSPFieldXElement(fieldTemplate, fieldModel);

            var typedFieldModel = fieldModel.WithAssertAndCast<NoteFieldDefinition>("model", value => value.RequireNotNull());

            if (typedFieldModel.NumberOfLines > 0)
                fieldTemplate.SetAttribute(BuiltInFieldAttributes.NumLines, typedFieldModel.NumberOfLines);

            fieldTemplate.SetAttribute(BuiltInFieldAttributes.RichText, typedFieldModel.RichText.ToString().ToUpper());
            fieldTemplate.SetAttribute(BuiltInFieldAttributes.RichTextMode, typedFieldModel.RichTextMode);
            fieldTemplate.SetAttribute(BuiltInFieldAttributes.AppendOnly, typedFieldModel.AppendOnly.ToString().ToUpper());
            fieldTemplate.SetAttribute(BuiltInFieldAttributes.UnlimitedLengthInDocumentLibrary, typedFieldModel.UnlimitedLengthInDocumentLibrary.ToString().ToUpper());
        }
        protected override void ProcessFieldProperties(SPField field, FieldDefinition fieldModel)
        {
            // let base setting be setup
            base.ProcessFieldProperties(field, fieldModel);

            var typedFieldModel = fieldModel.WithAssertAndCast<ChoiceFieldDefinition>("model", value => value.RequireNotNull());
            var typedField = field as SPFieldChoice;
           
            if (!string.IsNullOrEmpty(fieldModel.ValidationMessage))
                field.ValidationMessage = fieldModel.ValidationMessage;

            if (!string.IsNullOrEmpty(fieldModel.ValidationFormula))
                field.ValidationFormula = fieldModel.ValidationFormula;
        }
        protected override void ProcessSPFieldXElement(XElement fieldTemplate, FieldDefinition fieldModel)
        {
            base.ProcessSPFieldXElement(fieldTemplate, fieldModel);

            var typedFieldModel = fieldModel.WithAssertAndCast<DependentLookupFieldDefinition>("model", value => value.RequireNotNull());
            fieldTemplate.SetAttribute(BuiltInFieldAttributes.Mult, typedFieldModel.AllowMultipleValues.ToString().ToUpper());

            SPField primaryField = GetPrimaryField(typedFieldModel);
            if (primaryField == null)
            {
                throw new SPMeta2Exception("PrimaryLookupField needs to be defined when creating a DependentLookupField");
            }

            fieldTemplate.SetAttribute(BuiltInFieldAttributes.FieldReference, primaryField.Id.ToString("B"));
        }
        protected override void ProcessFieldProperties(Field field, FieldDefinition fieldModel)
        {
            // let base setting be setup
            base.ProcessFieldProperties(field, fieldModel);

            var typedFieldModel = fieldModel.WithAssertAndCast<MultiChoiceFieldDefinition>("model", value => value.RequireNotNull());
            var typedField = field.Context.CastTo<FieldMultiChoice>(field);

            typedField.FillInChoice = typedFieldModel.FillInChoice;

            if (typedFieldModel.Choices.Count > 0)
            {
                typedField.Choices = typedFieldModel.Choices.ToArray();
            }
        }
        protected override void ProcessFieldProperties(SPField field, FieldDefinition fieldModel)
        {
            // let base setting be setup
            base.ProcessFieldProperties(field, fieldModel);

            var typedFieldModel = fieldModel.WithAssertAndCast<CalculatedFieldDefinition>("model", value => value.RequireNotNull());
            var typedField = field as SPFieldCalculated;

            typedField.Formula = typedFieldModel.Formula ?? string.Empty;

            if (typedFieldModel.ShowAsPercentage.HasValue)
                typedField.ShowAsPercentage = typedFieldModel.ShowAsPercentage.Value;

            typedField.OutputType = (SPFieldType)Enum.Parse(typeof(SPFieldType), typedFieldModel.OutputType);
        }
        protected override void ProcessFieldProperties(SPField field, FieldDefinition fieldModel)
        {
            // let base setting be setup
            base.ProcessFieldProperties(field, fieldModel);

            // process bcs field specific properties
            var bcsField = field.WithAssertAndCast<SPBusinessDataField>("field", value => value.RequireNotNull());
            var bcsFieldModel = fieldModel.WithAssertAndCast<BusinessDataFieldDefinition>("model", value => value.RequireNotNull());

            bcsField.SystemInstanceName = bcsFieldModel.SystemInstanceName;
            bcsField.EntityNamespace = bcsFieldModel.EntityNamespace;

            bcsField.EntityName = bcsFieldModel.EntityName;
            bcsField.BdcFieldName = bcsFieldModel.BdcFieldName;
        }
        protected override void ProcessFieldProperties(Field field, FieldDefinition fieldModel)
        {
            var context = CurrentHostClientContext;

            var taxFieldModel = fieldModel.WithAssertAndCast<TaxonomyFieldDefinition>("model", value => value.RequireNotNull());

            var termStore = LookupTermStore(CurrentHostClientContext, taxFieldModel, false);

            TermSet termSet = null;
            Term term = null;

            if (termStore != null)
            {
                termSet = LookupTermSet(CurrentHostClientContext, termStore, taxFieldModel);
                term = LookupTerm(CurrentHostClientContext, termStore, termSet, taxFieldModel);
            }

            var taxField = context.CastTo<TaxonomyField>(field);

            // let base setting be setup
            base.ProcessFieldProperties(taxField, fieldModel);

            taxField.AllowMultipleValues = taxFieldModel.IsMulti;

            if (taxFieldModel.Open.HasValue)
                taxField.Open = taxFieldModel.Open.Value;

            if (taxFieldModel.IsPathRendered.HasValue)
                taxField.IsPathRendered = taxFieldModel.IsPathRendered.Value;

            if (taxFieldModel.CreateValuesInEditForm.HasValue)
                taxField.CreateValuesInEditForm = taxFieldModel.CreateValuesInEditForm.Value;

            taxField.Description = string.IsNullOrEmpty(taxFieldModel.Description)
               ? string.Empty
               : taxFieldModel.Description;

            taxField.Required = taxFieldModel.Required;

            if (termStore != null)
                taxField.SspId = termStore.Id;

            if (termSet != null)
                taxField.TermSetId = termSet.Id;

            if (term != null)
                taxField.AnchorId = term.Id;
        }