private void ctor(string name, int order, Type type, IEnumerable <FieldAttribute> attrs, PropertyInfo memberInfo = null) { if (name.IsNullOrWhiteSpace() || type == null || attrs == null) { throw new DataException(StringConsts.ARGUMENT_ERROR + "FieldDef.ctor(..null..)"); } m_Name = name; m_Order = order; m_Type = type; m_Attrs = new List <FieldAttribute>(attrs); if (m_Attrs.Count < 1) { throw new DataException(StringConsts.CRUD_FIELDDEF_ATTR_MISSING_ERROR.Args(name)); } //add ANY_TARGET attribute if (!m_Attrs.Any(a => a.TargetName == TargetedAttribute.ANY_TARGET)) { var isAnyKey = m_Attrs.Any(a => a.Key); var ata = new FieldAttribute(FieldAttribute.ANY_TARGET, key: isAnyKey); m_Attrs.Add(ata); } m_MemberInfo = memberInfo; if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable <>)) { m_NonNullableType = type.GetGenericArguments()[0]; } else { m_NonNullableType = type; } m_AnyTargetKey = this[null].Key; }
protected Exception CheckMinMax(FieldAttribute atr, string fName, IComparable val) { if (atr.Min != null) { var bound = atr.Min as IComparable; if (bound != null) { var tval = val.GetType(); bound = Convert.ChangeType(bound, tval) as IComparable; if (val.CompareTo(bound) < 0) { return(new FieldValidationException(Schema.Name, fName, StringConsts.CRUD_FIELD_VALUE_MIN_BOUND_ERROR)); } } } if (atr.Max != null) { var bound = atr.Max as IComparable; if (bound != null) { var tval = val.GetType(); bound = Convert.ChangeType(bound, tval) as IComparable; if (val.CompareTo(bound) > 0) { return(new FieldValidationException(Schema.Name, fName, StringConsts.CRUD_FIELD_VALUE_MAX_BOUND_ERROR)); } } } return(null); }
protected virtual Exception CheckValueMinMax(string targetName, Schema.FieldDef fdef, FieldAttribute atr, object value) { if (!(value is IComparable val)) { return(null); } if (atr.Min != null) { var bound = atr.Min as IComparable; if (bound != null) { var tval = val.GetType(); bound = Convert.ChangeType(bound, tval) as IComparable; if (val.CompareTo(bound) < 0) { return(new FieldValidationException(Schema.DisplayName, fdef.Name, StringConsts.CRUD_FIELD_VALUE_MIN_BOUND_ERROR)); } } } if (atr.Max != null) { var bound = atr.Max as IComparable; if (bound != null) { var tval = val.GetType(); bound = Convert.ChangeType(bound, tval) as IComparable; if (val.CompareTo(bound) > 0) { return(new FieldValidationException(Schema.DisplayName, fdef.Name, StringConsts.CRUD_FIELD_VALUE_MAX_BOUND_ERROR)); } } } return(null); }
protected virtual Exception CheckValueList(string targetName, Schema.FieldDef fdef, FieldAttribute atr, object value) { if (atr.HasValueList)//check dictionary { var parsed = atr.ParseValueList(); if (isSimpleKeyStringMap(parsed)) { var fv = value.ToString(); if (!parsed.ContainsKey(fv)) { return(new FieldValidationException(Schema.DisplayName, fdef.Name, StringConsts.CRUD_FIELD_VALUE_IS_NOT_IN_LIST_ERROR.Args(fv.TakeFirstChars(16, "..")))); } } } //check dynamic value list var dynValueList = GetDynamicFieldValueList(fdef, targetName, null); if (dynValueList != null)//check dictionary { var fv = value.ToString(); if (!dynValueList.ContainsKey(fv)) { return(new FieldValidationException(Schema.DisplayName, fdef.Name, StringConsts.CRUD_FIELD_VALUE_IS_NOT_IN_LIST_ERROR.Args(fv.TakeFirstChars(16, "..")))); } } return(null); }
protected virtual Exception CheckValueIValidatable(string targetName, Schema.FieldDef fdef, FieldAttribute atr, object value) { if (value is IValidatable validatable) { return(validatable.Validate(targetName)); } if (value is IDictionary dict)//Dictionary<string, IValidatable> { foreach (var v in dict.Values) { if (v is IValidatable vv) { var error = vv.Validate(targetName); if (error != null) { return(error); } } } } else if (value is IEnumerable enm)//List<IValidatable>, IValidatable[] { foreach (var v in enm) { if (!(v is IValidatable vv)) { continue; } var error = vv.Validate(targetName); if (error != null) { return(error); } } } return(null); }
protected virtual Exception CheckValueKind(string targetName, Schema.FieldDef fdef, FieldAttribute atr, object value) { if (atr.Kind == DataKind.ScreenName) { if (!Azos.Text.DataEntryUtils.CheckScreenName(value.ToString())) { return(new FieldValidationException(Schema.DisplayName, 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.DisplayName, 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.DisplayName, fdef.Name, StringConsts.CRUD_FIELD_VALUE_PHONE_ERROR)); } } return(null); }
protected virtual Exception CheckValueLength(string targetName, Schema.FieldDef fdef, FieldAttribute atr, object value) { if (atr.MinLength > 0) { if (value is IEnumerable <object> eobj && eobj.Count() < atr.MinLength) { return(new FieldValidationException(Schema.DisplayName, fdef.Name, StringConsts.CRUD_FIELD_VALUE_MIN_LENGTH_ERROR.Args(atr.MinLength))); } 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 (value is IEnumerable <object> eobj && eobj.Count() > atr.MaxLength) { return(new FieldValidationException(Schema.DisplayName, fdef.Name, StringConsts.CRUD_FIELD_VALUE_MAX_LENGTH_ERROR.Args(atr.MaxLength))); } 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); }
protected virtual (bool hasValue, Exception error) CheckValueRequired(string targetName, Schema.FieldDef fdef, FieldAttribute atr, object value) { if (value == null || (value is string strv && strv.IsNullOrWhiteSpace()) || (value is GDID gdid && gdid.IsZero)) { 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); }
private Schema(Type tdoc) { lock (s_TypeLatch) { if (s_TypeLatch.Contains(tdoc)) { throw new DataException(StringConsts.CRUD_TYPED_DOC_RECURSIVE_FIELD_DEFINITION_ERROR.Args(tdoc.FullName)); } s_TypeLatch.Add(tdoc); try { m_Name = tdoc.AssemblyQualifiedName; var tattrs = tdoc.GetCustomAttributes(typeof(SchemaAttribute), false).Cast <SchemaAttribute>(); tattrs.ForEach(a => a.StopPropAssignmentTracking()); m_SchemaAttrs = new List <SchemaAttribute>(tattrs); //20191026 DKh. Expand resource references in Descriptions m_SchemaAttrs.ForEach(a => { a.ExpandResourceReferencesRelativeTo(tdoc, null); a.Seal(); }); m_FieldDefs = new OrderedRegistry <FieldDef>(); var props = GetFieldMembers(tdoc); var order = 0; foreach (var prop in props) { var fattrs = prop.GetCustomAttributes(typeof(FieldAttribute), false) .Cast <FieldAttribute>() .ToArray(); fattrs.ForEach(a => a.StopPropAssignmentTracking()); //Interpret [Field(CloneFromType)] for (var i = 0; i < fattrs.Length; i++) { var attr = fattrs[i]; if (attr.CloneFromDocType == null) { //20190831 DKh. Expand resource references in Descriptions attr.ExpandResourceReferencesRelativeTo(tdoc, prop.Name); continue; } if (fattrs.Length > 1) { throw new DataException(StringConsts.CRUD_TYPED_DOC_SINGLE_CLONED_FIELD_ERROR.Args(tdoc.FullName, prop.Name)); } var clonedSchema = Schema.GetForTypedDoc(attr.CloneFromDocType); var clonedDef = clonedSchema[prop.Name]; if (clonedDef == null) { throw new DataException(StringConsts.CRUD_TYPED_DOC_CLONED_FIELD_NOTEXISTS_ERROR.Args(tdoc.FullName, prop.Name)); } fattrs = clonedDef.Attrs.ToArray();//replace these attrs from the cloned target break; } FieldAttribute.FixupInheritedTargets($"{tdoc.Name}.{prop.Name}", fattrs); var fdef = new FieldDef(prop.Name, order, prop.PropertyType, fattrs, prop); m_FieldDefs.Register(fdef); order++; } s_TypedRegistry.Register(this); m_TypedDocType = tdoc; } finally { s_TypeLatch.Remove(tdoc); } }//lock }
/// <summary> ///Dynamic value lists override static ones: /// * If a dynamic value list is null then hard coded ValueList is enforced if it is specified; /// * If a dynamic list is non-null then it is enforced if it is not blank, otherwise nothing is checked; /// Therefore: you may return an empty non-null dynamic list to prevent application of ValueList check for specific field/target /// </summary> protected virtual Exception CheckValueList(string targetName, Schema.FieldDef fdef, FieldAttribute atr, object value) { //try to obtain dynamic value list var dynValueList = GetDynamicFieldValueList(fdef, targetName, null); if (dynValueList != null)//check dynamic list is supplied { if (dynValueList.Count == 0) { return(null); //Nothing to check against; this is used to return empty list to override ValueList list } var fv = value.ToString(); if (!dynValueList.ContainsKey(fv)) { return(new FieldValidationException(Schema.DisplayName, fdef.Name, StringConsts.CRUD_FIELD_VALUE_IS_NOT_IN_LIST_ERROR.Args(fv.TakeFirstChars(9, "..")))); } } else if (atr.HasValueList)//check ValueList dictionary { var parsed = atr.ParseValueList(); var fv = value.ToString(); if (!parsed.ContainsKey(fv)) { return(new FieldValidationException(Schema.DisplayName, fdef.Name, StringConsts.CRUD_FIELD_VALUE_IS_NOT_IN_LIST_ERROR.Args(fv.TakeFirstChars(9, "..")))); } } return(null); }
protected virtual Exception CheckValueLength(string targetName, Schema.FieldDef fdef, FieldAttribute atr, object value) { if (atr.MinLength < 1 && atr.MaxLength < 1) { 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); }
internal FieldDescriptor(string name, Schema.FieldDef def, FieldAttribute atr) { TargetFieldName = name; FieldDef = def; Attr = atr; }
protected virtual ValidState CheckValueIValidatable(ValidState state, Schema.FieldDef fdef, FieldAttribute atr, object value, string scope) { //20200517 DKh using ObjectGraph.Scope() //-------------------------------------- if (value is IValidatable validatable) { var got = ObjectGraph.Scope("Doc.CheckValueIValidatable.IVal", //name of the state machine this, //reference to cycle subject - the document itself validatable, //arg1 state, //arg2 GetInnerScope(fdef, scope), //arg3 body: validateIValidatable ); //machine return(got.OK ? got.result : state); } //precedence of IFs is important, IDictionary is IEnumerable if (value is IDictionary dict)//Dictionary<string, IValidatable> { var got = ObjectGraph.Scope("Doc.CheckValueIValidatable.IDict", this, dict, state, GetInnerScope(fdef, scope), body: validateIDictionary ); return(got.OK ? got.result : state); } if (value is IEnumerable enm)//List<IValidatable>, IValidatable[] { var got = ObjectGraph.Scope("Doc.CheckValueIValidatable.IEnum", this, enm, state, GetInnerScope(fdef, scope), body: validateIEnumerable ); return(got.OK ? got.result : state); } return(state); }
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); }
protected virtual Exception CheckValueRegExp(string targetName, Schema.FieldDef fdef, FieldAttribute atr, object value) { 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.DisplayName, fdef.Name, StringConsts.CRUD_FIELD_VALUE_REGEXP_ERROR.Args(atr.FormatDescription ?? "Input format: {0}".Args(atr.FormatRegExp)))); } } } return(null); }
public FieldDef(string name, Type type, FieldAttribute attr) { ctor(name, 0, type, new[] { attr }, null); }
private static void inheritAttribute(FieldAttribute parent, FieldAttribute self, string callSite) { //merge attributes from parent into self prop by prop foreach (var pi in ALL_PROPS) { if (pi.Name == nameof(MetadataContent)) { if (self.MetadataContent.IsNullOrWhiteSpace()) { self.MetadataContent = parent.MetadataContent; } else if (parent.MetadataContent.IsNotNullOrWhiteSpace()) { //merge var conf1 = ParseMetadataContent(parent.MetadataContent, callSite); var conf2 = ParseMetadataContent(self.MetadataContent, callSite); var merged = new LaconicConfiguration(); merged.CreateFromMerge(conf1, conf2); self.MetadataContent = merged.SaveToString(); } continue; }//metadata merge if (pi.Name == nameof(ValueList)) { if (self.ValueList == null && self.PropertyWasAssigned(nameof(ValueList)))//explicit reset { self.ValueList = null; continue; } else if (!self.HasValueList) { self.ValueList = parent.ValueList; } else if (parent.HasValueList) { //merge var vl1 = parent.ParseValueList(true); var vl2 = self.ParseValueList(true); vl2.Append(vl1);//merge missing in self from parent //remove all that start with REMOVE // to remove a key include an override with: `keyABC: #del#` (item keyed on `keyABC` will be removed) const string DELETE = "#del#"; vl2.Where(kvp => kvp.Value.AsString().EqualsOrdIgnoreCase(DELETE)) .ToArray() .ForEach(kvp => vl2.Remove(kvp.Key)); self.ValueList = BuildValueListString(vl2);//reconstitute jsonmap back into string } continue; } if (self.PropertyWasAssigned(pi.Name)) { continue; //was overridden } pi.SetValue(self, pi.GetValue(parent)); //set value } }