/// <summary> /// Override to perform dynamic substitute of field def for the specified field. /// This method is used by client ui/scaffolding to extract dynamic definition for a field /// (i.e. field description, requirement, value list etc.) as dictated by business logic. /// This method IS NOT used by doc validation, only by client that feeds from doc metadata. /// The default implementation returns the original field def, you can return a substituted field def /// for a specific business logic need /// </summary> public virtual Schema.FieldDef GetClientFieldDef(Schema.FieldDef fdef, string targetName, Atom isoLang) { return(fdef); }
/// <summary> /// Override to perform dynamic substitute of field value for the specified field. /// This method is used by client ui/scaffolding to extract field values for a field as dictated by business logic. /// This method IS NOT used by doc validation, only by client that feeds from doc metadata. /// The default implementation returns the original GetFieldValue(fdef), you can return a substituted field value /// per particular business logic /// </summary> public virtual object GetClientFieldValue(Schema.FieldDef fdef, string targetName, Atom isoLang) { return(GetFieldValue(fdef)); }
private bool isFieldDefLoaded(string target, Schema.FieldDef def) { var atr = def[target]; return((atr == null) ? true : (atr.StoreFlag == StoreFlag.LoadAndStore || atr.StoreFlag == StoreFlag.OnlyLoad)); }
/// <summary> /// Override to get a list of permissible field values for the specified field for the specified target. /// This method is used by validation to extract dynamic pick list entries form data stores /// as dictated by business logic. The override must be efficient and typically rely on caching of /// values gotten from the datastore. This method should NOT return more than a manageable limited number of records (e.g. less than 100) /// in a single form drop-down/combo, as the large lookups are expected to be implemented using complex lookup models (e.g. dialog boxes in UI). /// Return a null to indicate an absence of a value list for the specified field. /// Return an empty JsonDataMap to indicate that dynamic value list is present, but there is nothing to check against - this is used to /// override a static fdef.ValueList which would be enforced otherwise. /// </summary> public virtual JsonDataMap GetDynamicFieldValueList(Schema.FieldDef fdef, string targetName, Atom isoLang) { return(null); }
public override void SetFieldValue(Schema.FieldDef fdef, object value) { value = ConvertFieldValueToDef(fdef, value); fdef.SetPropertyValue(this, value); }
private void field(string targetName, Schema.FieldDef def, IMetadataGenerator context, ConfigSectionNode data, TypedDoc doc) { var fname = def.GetBackendNameForTarget(targetName, out var fatr); if (fatr == null) { return; } if (context.DetailLevel > MetadataDetailLevel.Public) { data.AddAttributeNode("prop-name", def.Name); data.AddAttributeNode("prop-type", def.Type.AssemblyQualifiedName); data.AddAttributeNode("non-ui", fatr.NonUI); data.AddAttributeNode("is-arow", fatr.IsArow); data.AddAttributeNode("store-flag", fatr.StoreFlag); data.AddAttributeNode("backend-type", fatr.BackendType); //try to disclose ALL metadata (as we are above PUBLIC) if (fatr.Metadata != null && fatr.Metadata.Exists) { var metad = data.AddChildNode("meta"); metad.MergeSections(fatr.Metadata); metad.MergeAttributes(fatr.Metadata); } } else //try to disclose pub-only metadata { var pubSection = context.PublicMetadataSection; if (fatr.Metadata != null && pubSection.IsNotNullOrWhiteSpace()) { var metasrc = fatr.Metadata[pubSection];//<-- pub metadata only if (metasrc.Exists) { var metad = data.AddChildNode("meta"); metad.MergeSections(metasrc); metad.MergeAttributes(metasrc); } } } data.AddAttributeNode("name", fname); data.AddAttributeNode("type", context.AddTypeToDescribe(def.Type)); data.AddAttributeNode("order", def.Order); if (fatr.Description.IsNotNullOrWhiteSpace()) { data.AddAttributeNode("description", fatr.Description); } data.AddAttributeNode("key", fatr.Key); data.AddAttributeNode("kind", fatr.Kind); data.AddAttributeNode("required", fatr.Required); data.AddAttributeNode("visible", fatr.Required); data.AddAttributeNode("case", fatr.CharCase); if (fatr.Default != null) { data.AddAttributeNode("default", fatr.Default); } if (fatr.DisplayFormat.IsNotNullOrWhiteSpace()) { data.AddAttributeNode("display-format", fatr.DisplayFormat); } if (fatr.FormatRegExp.IsNotNullOrWhiteSpace()) { data.AddAttributeNode("format-reg-exp", fatr.FormatRegExp); } if (fatr.FormatDescription.IsNotNullOrWhiteSpace()) { data.AddAttributeNode("format-description", fatr.FormatDescription); } if (fatr.Max != null) { data.AddAttributeNode("max", fatr.Max); } if (fatr.Min != null) { data.AddAttributeNode("min", fatr.Min); } if (fatr.MinLength > 0 || fatr.MaxLength > 0) { data.AddAttributeNode("min-len", fatr.MinLength); } if (fatr.MinLength > 0 || fatr.MaxLength > 0) { data.AddAttributeNode("max-len", fatr.MaxLength); } //add values from field attribute .ValueList property var nvlist = new Lazy <ConfigSectionNode>(() => data.AddChildNode("value-list")); if (fatr.HasValueList) { fatr.ParseValueList().ForEach(item => nvlist.Value.AddAttributeNode(item.Key, item.Value)); } //if doc!=null call doc.GetClientFieldValueList on the instance to get values from Database lookups etc... if (doc != null) { var lookup = doc.GetDynamicFieldValueList(def, targetName, Atom.ZERO); if (lookup != null)//non-null blank lookup is treated as blank lookup overshadowing the hard-coded choices from .ValueList { if (nvlist.IsValueCreated) { nvlist.Value.DeleteAllAttributes(); } lookup.ForEach(item => nvlist.Value.AddAttributeNode(item.Key, item.Value)); } } }
protected virtual Exception CheckValueLength(string targetName, Schema.FieldDef fdef, FieldAttribute atr, object value, string scope) { if (atr.MinLength < 1 && atr.MaxLength < 1) { return(null); } if (value is ILengthCheck lc) { if (atr.MinLength > 0 && !lc.CheckMinLength(targetName, atr.MinLength)) { return(new FieldValidationException(Schema.DisplayName, fdef.Name, StringConsts.CRUD_FIELD_VALUE_MIN_LENGTH_ERROR.Args(atr.MinLength))); } if (atr.MaxLength > 0 && !lc.CheckMaxLength(targetName, atr.MaxLength)) { return(new FieldValidationException(Schema.DisplayName, fdef.Name, StringConsts.CRUD_FIELD_VALUE_MAX_LENGTH_ERROR.Args(atr.MaxLength))); } return(null); } var isString = value is string; var eobj = value as IEnumerable; var ecount = !isString && eobj != null?eobj.Cast <object>().Count() : -1; if (atr.MinLength > 0) { if (ecount >= 0) { if (ecount < atr.MinLength) { return(new FieldValidationException(Schema.DisplayName, fdef.Name, StringConsts.CRUD_FIELD_VALUE_MIN_LENGTH_ERROR.Args(atr.MinLength))); } } else { if (value.ToString().Length < atr.MinLength) { return(new FieldValidationException(Schema.DisplayName, fdef.Name, StringConsts.CRUD_FIELD_VALUE_MIN_LENGTH_ERROR.Args(atr.MinLength))); } } } if (atr.MaxLength > 0) { if (ecount >= 0) { if (ecount > atr.MaxLength) { return(new FieldValidationException(Schema.DisplayName, fdef.Name, StringConsts.CRUD_FIELD_VALUE_MAX_LENGTH_ERROR.Args(atr.MaxLength))); } } else { if (value.ToString().Length > atr.MaxLength) { return(new FieldValidationException(Schema.DisplayName, fdef.Name, StringConsts.CRUD_FIELD_VALUE_MAX_LENGTH_ERROR.Args(atr.MaxLength))); } } } return(null); }
/// <summary> /// Sets value of the field, for typeddocs it accesses property using reflection/lambda; for dynamic rows it sets data into /// row buffer array using field index(order) /// </summary> public abstract void SetFieldValue(Schema.FieldDef fdef, object value);
/// <summary> /// Override to perform dynamic lookup of field value list for the specified field. /// This method is used by client ui/scaffolding to extract dynamic lookup values /// as dictated by business logic. This method IS NOT used by doc validation, only by client /// that feeds from doc metadata. /// This is a simplified version of GetClientFieldDef /// </summary> public virtual JSONDataMap GetClientFieldValueList(Schema.FieldDef fdef, string targetName, string isoLang) { return(null); }
protected virtual (bool hasValue, Exception error) CheckValueRequired(string targetName, Schema.FieldDef fdef, FieldAttribute atr, object value, string scope) { var missing = (value == null) || (value is string strv && strv.IsNullOrWhiteSpace()) || //string null, or whitespace are treated as missing (value is IRequiredCheck ireq && !ireq.CheckRequired(targetName)); if (missing) { if (atr.Required) { return(false, new FieldValidationException(Schema.DisplayName, fdef.Name, StringConsts.CRUD_FIELD_VALUE_REQUIRED_ERROR)); } return(false, null); //no other validations are needed as field is null anyway } return(true, null); }
/// <summary> /// Validates row field using Schema.FieldDef settings. /// This method is invoked by base Validate() implementation. /// The method is not expected to throw exception in case of failed validation, rather return exception instance because /// throwing exception really hampers validation performance when many rows need to be validated /// </summary> public virtual Exception ValidateField(string targetName, Schema.FieldDef fdef) { if (fdef == null) { throw new FieldValidationException(Schema.Name, CoreConsts.NULL_STRING, StringConsts.ARGUMENT_ERROR + ".ValidateField(fdef=null)"); } var atr = fdef[targetName]; if (atr == null) { return(null); } var value = GetFieldValue(fdef); if (value == null || (value is string && ((string)value).IsNullOrWhiteSpace()) || (value is GDID && ((GDID)value).IsZero) ) { if (atr.Required) { return(new FieldValidationException(Schema.Name, fdef.Name, StringConsts.CRUD_FIELD_VALUE_REQUIRED_ERROR)); } return(null); } if (value is IValidatable) { return(((IValidatable)value).Validate(targetName)); } var enumerableIValidatable = value as IEnumerable <IValidatable>; if (enumerableIValidatable != null)//List<IValidatable>, IValidatable[] { foreach (var v in enumerableIValidatable) { if (v == null) { continue; } var error = v.Validate(targetName); if (error != null) { return(error); } } return(null); } var enumerableKVP = value as IEnumerable <KeyValuePair <string, IValidatable> >; if (enumerableKVP != null)//Dictionary<string, IValidatable> { foreach (var kv in enumerableKVP) { var v = kv.Value; if (v == null) { continue; } var error = v.Validate(targetName); if (error != null) { return(error); } } return(null); } if (atr.HasValueList)//check dictionary { var parsed = atr.ParseValueList(); if (isSimpleKeyStringMap(parsed)) { if (!parsed.ContainsKey(value.ToString())) { return(new FieldValidationException(Schema.Name, fdef.Name, StringConsts.CRUD_FIELD_VALUE_IS_NOT_IN_LIST_ERROR)); } } } if (atr.MinLength > 0) { if (value.ToString().Length < atr.MinLength) { return(new FieldValidationException(Schema.Name, fdef.Name, StringConsts.CRUD_FIELD_VALUE_MIN_LENGTH_ERROR)); } } if (atr.MaxLength > 0) { if (value.ToString().Length > atr.MaxLength) { return(new FieldValidationException(Schema.Name, fdef.Name, StringConsts.CRUD_FIELD_VALUE_MAX_LENGTH_ERROR)); } } if (atr.Kind == DataKind.ScreenName) { if (!Azos.Text.DataEntryUtils.CheckScreenName(value.ToString())) { return(new FieldValidationException(Schema.Name, fdef.Name, StringConsts.CRUD_FIELD_VALUE_SCREEN_NAME_ERROR)); } } else if (atr.Kind == DataKind.EMail) { if (!Azos.Text.DataEntryUtils.CheckEMail(value.ToString())) { return(new FieldValidationException(Schema.Name, fdef.Name, StringConsts.CRUD_FIELD_VALUE_EMAIL_ERROR)); } } else if (atr.Kind == DataKind.Telephone) { if (!Azos.Text.DataEntryUtils.CheckTelephone(value.ToString())) { return(new FieldValidationException(Schema.Name, fdef.Name, StringConsts.CRUD_FIELD_VALUE_PHONE_ERROR)); } } if (value is IComparable) { var error = CheckMinMax(atr, fdef.Name, (IComparable)value); if (error != null) { return(error); } } if (atr.FormatRegExp.IsNotNullOrWhiteSpace()) { //For those VERY RARE cases when RegExpFormat may need to be applied to complex types, i.e. StringBuilder //set the flag in metadata to true, otherwise regexp gets matched only for STRINGS var complex = atr.Metadata == null? false : atr.Metadata .AttrByName("validate-format-regexp-complex-types") .ValueAsBool(false); if (complex || value is string) { if (!System.Text.RegularExpressions.Regex.IsMatch(value.ToString(), atr.FormatRegExp)) { return(new FieldValidationException(Schema.Name, fdef.Name, StringConsts.CRUD_FIELD_VALUE_REGEXP_ERROR.Args(atr.FormatDescription ?? "Input format: {0}".Args(atr.FormatRegExp)))); } } } return(null); }
private void field(Schema.FieldDef def, IMetadataGenerator context, ConfigSectionNode data, TypedDoc doc) { var fname = def.GetBackendNameForTarget(context.DataTargetName, out var fatr); if (fatr == null) { return; } if (context.DetailLevel > MetadataDetailLevel.Public) { data.AddAttributeNode("prop-name", def.Name); data.AddAttributeNode("prop-type", def.Type.AssemblyQualifiedName); data.AddAttributeNode("non-ui", fatr.NonUI); data.AddAttributeNode("is-arow", fatr.IsArow); data.AddAttributeNode("store-flag", fatr.StoreFlag); data.AddAttributeNode("backend-type", fatr.BackendType); if (fatr.Metadata != null) { data.AddChildNode(fatr.Metadata); } } data.AddAttributeNode("name", fname); data.AddAttributeNode("type", context.AddTypeToDescribe(def.Type)); data.AddAttributeNode("order", def.Order); if (fatr.Description.IsNotNullOrWhiteSpace()) { data.AddAttributeNode("description", fatr.Description); } data.AddAttributeNode("key", fatr.Key); if (def.Type == typeof(string)) { data.AddAttributeNode("kind", fatr.Kind); } data.AddAttributeNode("required", fatr.Required); data.AddAttributeNode("visible", fatr.Required); data.AddAttributeNode("case", fatr.CharCase); if (fatr.Default != null) { data.AddAttributeNode("default", fatr.Default); } if (fatr.DisplayFormat.IsNotNullOrWhiteSpace()) { data.AddAttributeNode("display-format", fatr.DisplayFormat); } if (fatr.FormatRegExp.IsNotNullOrWhiteSpace()) { data.AddAttributeNode("format-reg-exp", fatr.FormatRegExp); } if (fatr.FormatDescription.IsNotNullOrWhiteSpace()) { data.AddAttributeNode("format-description", fatr.FormatDescription); } if (fatr.Max != null) { data.AddAttributeNode("max", fatr.Max); } if (fatr.Min != null) { data.AddAttributeNode("min", fatr.Min); } if (fatr.MinLength > 0 || fatr.MaxLength > 0) { data.AddAttributeNode("min-len", fatr.MinLength); } if (fatr.MinLength > 0 || fatr.MaxLength > 0) { data.AddAttributeNode("max-len", fatr.MaxLength); } //add values from field attribute .ValueList property var nvlist = new Lazy <ConfigSectionNode>(() => data.AddChildNode("value-list")); if (fatr.HasValueList) { fatr.ParseValueList().ForEach(item => nvlist.Value.AddAttributeNode(item.Key, item.Value)); } //if doc!=null call doc.GetClientFieldValueList on the instance to get values from Database lookups etc... if (doc != null) { var lookup = doc.GetClientFieldValueList(def, context.DataTargetName, null); if (lookup != null) { lookup.ForEach(item => nvlist.Value.AddAttributeNode(item.Key, item.Value)); } } }
internal FieldDescriptor(string name, Schema.FieldDef def, FieldAttribute atr) { TargetFieldName = name; FieldDef = def; Attr = atr; }
/// <summary> /// Override to perform custom transformation of value/add extra values to JSON output map. /// For example, this is used to normalize phone numbers by adding a field with `_normalized` suffix to every field containing a phone. /// Default base implementation just writes value into named map key. FieldDef is null for amorphous fields /// </summary> protected virtual void AddJsonSerializerField(Schema.FieldDef def, JsonWritingOptions options, Dictionary <string, object> jsonMap, string name, object value) { jsonMap[name] = value; }
protected string GetInnerScope(Schema.FieldDef fdef, string scope) => scope.IsNullOrWhiteSpace() ? fdef.Name : scope + "." + fdef.Name;
/// <summary> /// Gets value of the field, for typeddocs it accesses property using reflection; for dynamic rows it reads data from /// row buffer array using field index(order) /// </summary> public abstract object GetFieldValue(Schema.FieldDef fdef);
/// <summary> /// Validates document field using Schema.FieldDef settings. /// This method is invoked by base Validate() implementation. /// The method is not expected to throw exception in case of failed validation, rather return exception instance because /// throwing exception really impedes validation performance when many document instances need to be validated /// </summary> public virtual ValidState ValidateField(ValidState state, Schema.FieldDef fdef, string scope = null) { if (fdef == null) { throw new FieldValidationException(Schema.DisplayName, CoreConsts.NULL_STRING, StringConsts.ARGUMENT_ERROR + ".ValidateField(fdef=null)"); } var atr = fdef[state.TargetName]; if (atr == null) { return(state); //not found per target } var value = GetFieldValue(fdef); var(hasValue, error) = CheckValueRequired(state.TargetName, fdef, atr, value, scope); if (error != null) { return(new ValidState(state, error)); } if (!hasValue) { return(state); //nothing else left to check } state = CheckValueIValidatable(state, fdef, atr, value, scope); if (state.ShouldStop) { return(state); } error = CheckValueLength(state.TargetName, fdef, atr, value, scope); if (error != null) { state = new ValidState(state, error); if (state.ShouldStop) { return(state); } } error = CheckValueKind(state.TargetName, fdef, atr, value, scope); if (error != null) { state = new ValidState(state, error); if (state.ShouldStop) { return(state); } } error = CheckValueMinMax(state.TargetName, fdef, atr, value, scope); if (error != null) { state = new ValidState(state, error); if (state.ShouldStop) { return(state); } } error = CheckValueRegExp(state.TargetName, fdef, atr, value, scope); if (error != null) { state = new ValidState(state, error); if (state.ShouldStop) { return(state); } } //this is at the end as ValueList check might induce a database call to get a pick list (when it is not cached) error = CheckValueList(state.TargetName, fdef, atr, value, scope); if (error != null) { state = new ValidState(state, error); if (state.ShouldStop) { return(state); } } return(state); }
/// <summary> /// Converts field value to the type specified by Schema.FieldDef. For example converts GDID->ulong or ulong->GDID. /// This method can be overridden to perform custom handling of types, /// for example one can assign bool field as "Si" that would convert to TRUE. /// This method is called by SetFieldValue(...) before assigning actual field buffer /// </summary> /// <param name="fdef">Field being converted</param> /// <param name="value">Value to convert</param> /// <returns>Converted value before assignment to field buffer</returns> public virtual object ConvertFieldValueToDef(Schema.FieldDef fdef, object value) { if (value == DBNull.Value) { value = null; } if (value == null) { return(null); } var tv = value.GetType(); if (tv != fdef.NonNullableType && !fdef.NonNullableType.IsAssignableFrom(tv)) { if (value is ObjectValueConversion.TriStateBool) { var tsb = (ObjectValueConversion.TriStateBool)value; if (tsb == ObjectValueConversion.TriStateBool.Unspecified) { value = null; } else { value = tsb == ObjectValueConversion.TriStateBool.True; } return(value); } if (fdef.NonNullableType == typeof(ObjectValueConversion.TriStateBool)) { var nb = value.AsNullableBool(); if (!nb.HasValue) { value = ObjectValueConversion.TriStateBool.Unspecified; } else { value = nb.Value ? ObjectValueConversion.TriStateBool.True : ObjectValueConversion.TriStateBool.False; } return(value); } // 20150224 DKh, addedEra to GDID. Only GDIDS with ERA=0 can be converted to/from INT64 if (fdef.NonNullableType == typeof(GDID)) { if (tv == typeof(byte[]))//20151103 DKh GDID support for byte[] { value = new GDID((byte[])value); } else if (tv == typeof(string))//20160504 Spol GDID support for string { var sv = (string)value; if (sv.IsNotNullOrWhiteSpace()) { value = GDID.Parse((string)value); } else { value = fdef.Type == typeof(GDID?) ? (GDID?)null : GDID.ZERO; } } else { value = new GDID(0, (UInt64)Convert.ChangeType(value, typeof(UInt64))); } return(value); } if (tv == typeof(GDID)) { if (fdef.NonNullableType == typeof(byte[])) { value = ((GDID)value).Bytes; } else if (fdef.NonNullableType == typeof(string)) { value = value.ToString(); } else { var gdid = (GDID)value; if (gdid.Era != 0) { throw new DataException(StringConsts.CRUD_GDID_ERA_CONVERSION_ERROR.Args(fdef.Name, fdef.NonNullableType.Name)); } value = gdid.ID; } return(value); } // 20161026 Serge: handle values of enumerated field types if (fdef.NonNullableType.IsEnum) { value = value.AsString().AsType(fdef.NonNullableType); return(value); } value = Convert.ChangeType(value, fdef.NonNullableType); }//Types Differ return(value); }
public override void SetFieldValue(Schema.FieldDef fdef, object value) { value = ConvertFieldValueToDef(fdef, value); m_Data[fdef.Order] = value; }