static IEnumerable <Error> validateListElementsFields( Object containingComponent, IList list, FieldInfo listFieldInfo, bool hasNotNull, FieldHierarchy fieldHierarchy, IErrorFactory createError, Option <CustomObjectValidator> customObjectValidatorOpt, Option <UniqueValuesCache> uniqueValuesCache ) { var listItemType = listFieldInfo.FieldType.GetElementType(); var listItemIsUnityObject = unityObjectType.IsAssignableFrom(listItemType); if (listItemIsUnityObject) { if (hasNotNull && list.Contains(null)) { yield return(createError.nullField(fieldHierarchy.asString())); } } else { var index = 0; foreach (var listItem in list) { fieldHierarchy.stack.Push($"[{index}]"); var validationResults = validateFields( containingComponent, listItem, createError, customObjectValidatorOpt, fieldHierarchy, uniqueValuesCache ); foreach (var _err in validationResults) { yield return(_err); } fieldHierarchy.stack.Pop(); index++; } } }
static IEnumerable <Error> validateFields( Object containingComponent, object objectBeingValidated, IErrorFactory createError, Option <CustomObjectValidator> customObjectValidatorOpt, FieldHierarchy fieldHierarchy = null, Option <UniqueValuesCache> uniqueValuesCache = default ) { Option.ensureValue(ref uniqueValuesCache); fieldHierarchy = fieldHierarchy ?? new FieldHierarchy(); foreach (var onObjectValidatable in F.opt(objectBeingValidated as OnObjectValidate)) { // Try because custom validations can throw exceptions. var validateResult = F.doTry(() => // Force strict enumerable evaluation, because it might throw an exception while evaluating. onObjectValidatable.onObjectValidate(containingComponent).ToArray() ); if (validateResult.isSuccess) { foreach (var error in validateResult.__unsafeGet) { yield return(createError.custom(fieldHierarchy.asString(), error, true)); } } else { var error = validateResult.__unsafeException; yield return(createError.exceptionInCustomValidator(fieldHierarchy.asString(), error)); } } foreach (var unityEvent in F.opt(objectBeingValidated as UnityEventBase)) { var errors = checkUnityEvent(createError, fieldHierarchy.asString(), unityEvent); foreach (var error in errors) { yield return(error); } } foreach (var customValidator in customObjectValidatorOpt) { foreach (var _err in customValidator(containingComponent, objectBeingValidated)) { yield return(createError.custom(fieldHierarchy.asString(), _err, true)); } } var fields = getFilteredFields(objectBeingValidated); foreach (var fi in fields) { fieldHierarchy.stack.Push(fi.Name); var fieldValue = fi.GetValue(objectBeingValidated); var hasNonEmpty = fi.hasAttribute <NonEmptyAttribute>(); foreach (var cache in uniqueValuesCache) { foreach (var attribute in fi.getAttributes <UniqueValue>()) { cache.addCheckedField(attribute.category, fieldValue, containingComponent); } } if (fieldValue is string s) { if (fi.getAttributes <TextFieldAttribute>().Any(a => a.Type == TextFieldType.Tag)) { if (!UnityEditorInternal.InternalEditorUtility.tags.Contains(s)) { yield return(createError.badTextFieldTag(fieldHierarchy.asString())); } } if (s.isEmpty() && hasNonEmpty) { yield return(createError.emptyString(fieldHierarchy.asString())); } } if (fi.isSerializable()) { var hasNotNull = fi.hasAttribute <NotNullAttribute>(); // Sometimes we get empty unity object. Equals catches that if (fieldValue == null || fieldValue.Equals(null)) { if (hasNotNull) { yield return(createError.nullField(fieldHierarchy.asString())); } } else { if (fieldValue is IList list) { if (list.Count == 0 && hasNonEmpty) { yield return(createError.emptyCollection(fieldHierarchy.asString())); } var fieldValidationResults = validateListElementsFields( containingComponent, list, fi, hasNotNull, fieldHierarchy, createError, customObjectValidatorOpt, uniqueValuesCache ); foreach (var _err in fieldValidationResults) { yield return(_err); } } else { var fieldType = fi.FieldType; // Check non-primitive serialized fields. if ( !fieldType.IsPrimitive && fieldType.hasAttribute <SerializableAttribute>() ) { var validationErrors = validateFields( containingComponent, fieldValue, createError, customObjectValidatorOpt, fieldHierarchy, uniqueValuesCache ); foreach (var _err in validationErrors) { yield return(_err); } } } } } fieldHierarchy.stack.Pop(); } }
static IEnumerable <Error> validateFields( Object containingComponent, object objectBeingValidated, IErrorFactory createError, Option <CustomObjectValidator> customObjectValidatorOpt, FieldHierarchy fieldHierarchy = null ) { fieldHierarchy = fieldHierarchy ?? new FieldHierarchy(); foreach (var onObjectValidatable in F.opt(objectBeingValidated as OnObjectValidate)) { foreach (var error in onObjectValidatable.onObjectValidate(containingComponent)) { yield return(createError.custom(fieldHierarchy.asString(), error)); } } foreach (var customValidator in customObjectValidatorOpt) { foreach (var _err in customValidator(containingComponent, objectBeingValidated)) { yield return(createError.custom(fieldHierarchy.asString(), _err)); } } var fields = getFilteredFields(objectBeingValidated); foreach (var fi in fields) { fieldHierarchy.stack.Push(fi.Name); if (fi.FieldType == typeof(string)) { if (fi.getAttributes <TextFieldAttribute>().Any(a => a.Type == TextFieldType.Tag)) { var fieldValue = (string)fi.GetValue(objectBeingValidated); if (!UnityEditorInternal.InternalEditorUtility.tags.Contains(fieldValue)) { yield return(createError.badTextFieldTag(fieldHierarchy.asString())); } } } if (fi.isSerializable()) { var fieldValue = fi.GetValue(objectBeingValidated); var hasNotNull = fi.hasAttribute <NotNullAttribute>(); // Sometimes we get empty unity object. Equals catches that if (fieldValue == null || fieldValue.Equals(null)) { if (hasNotNull) { yield return(createError.nullField(fieldHierarchy.asString())); } } else { var listOpt = F.opt(fieldValue as IList); if (listOpt.isSome) { var list = listOpt.get; if (list.Count == 0 && fi.hasAttribute <NonEmptyAttribute>()) { yield return(createError.emptyCollection(fieldHierarchy.asString())); } var fieldValidationResults = validateFields( containingComponent, list, fi, hasNotNull, fieldHierarchy, createError, customObjectValidatorOpt ); foreach (var _err in fieldValidationResults) { yield return(_err); } } else { var fieldType = fi.FieldType; // Check non-primitive serialized fields. if ( !fieldType.IsPrimitive && fieldType.hasAttribute <SerializableAttribute>() ) { var validationErrors = validateFields( containingComponent, fieldValue, createError, customObjectValidatorOpt, fieldHierarchy ); foreach (var _err in validationErrors) { yield return(_err); } } } } } fieldHierarchy.stack.Pop(); } }