1 package com.networknt.schema; 2 3 import com.fasterxml.jackson.databind.JsonNode; 4 import com.networknt.schema.i18n.MessageSource; 5 import com.networknt.schema.utils.StringUtils; 6 7 import java.util.Collections; 8 import java.util.LinkedHashMap; 9 import java.util.Map; 10 import java.util.Objects; 11 12 public abstract class ValidationMessageHandler { 13 protected final MessageSource messageSource; 14 protected ErrorMessageType errorMessageType; 15 16 protected SchemaLocation schemaLocation; 17 protected JsonNodePath evaluationPath; 18 protected JsonSchema evaluationParentSchema; 19 20 protected JsonSchema parentSchema; 21 22 protected boolean customErrorMessagesEnabled; 23 protected Map<String, String> errorMessage; 24 25 protected Keyword keyword; 26 ValidationMessageHandler(ErrorMessageType errorMessageType, boolean customErrorMessagesEnabled, MessageSource messageSource, Keyword keyword, JsonSchema parentSchema, SchemaLocation schemaLocation, JsonNodePath evaluationPath)27 protected ValidationMessageHandler(ErrorMessageType errorMessageType, boolean customErrorMessagesEnabled, 28 MessageSource messageSource, Keyword keyword, JsonSchema parentSchema, SchemaLocation schemaLocation, 29 JsonNodePath evaluationPath) { 30 this.errorMessageType = errorMessageType; 31 this.messageSource = messageSource; 32 this.schemaLocation = Objects.requireNonNull(schemaLocation); 33 this.evaluationPath = Objects.requireNonNull(evaluationPath); 34 this.parentSchema = parentSchema; 35 this.customErrorMessagesEnabled = customErrorMessagesEnabled; 36 updateKeyword(keyword); 37 } 38 39 /** 40 * Copy constructor. 41 * 42 * @param copy to copy from 43 */ ValidationMessageHandler(ValidationMessageHandler copy)44 protected ValidationMessageHandler(ValidationMessageHandler copy) { 45 this.messageSource = copy.messageSource; 46 this.errorMessageType = copy.errorMessageType; 47 this.schemaLocation = copy.schemaLocation; 48 this.evaluationPath = copy.evaluationPath; 49 this.parentSchema = copy.parentSchema; 50 this.evaluationParentSchema = copy.evaluationParentSchema; 51 this.customErrorMessagesEnabled = copy.customErrorMessagesEnabled; 52 this.errorMessage = copy.errorMessage; 53 this.keyword = copy.keyword; 54 } 55 message()56 protected MessageSourceValidationMessage.Builder message() { 57 return MessageSourceValidationMessage.builder(this.messageSource, this.errorMessage, (message, failFast) -> { 58 if (failFast) { 59 throw new FailFastAssertionException(message); 60 } 61 }).code(getErrorMessageType().getErrorCode()).schemaLocation(this.schemaLocation) 62 .evaluationPath(this.evaluationPath).type(this.keyword != null ? this.keyword.getValue() : null) 63 .messageKey(getErrorMessageType().getErrorCodeValue()); 64 } 65 66 protected ErrorMessageType getErrorMessageType() { 67 return this.errorMessageType; 68 } 69 70 protected void parseErrorCode(String errorCodeKey) { 71 if (errorCodeKey != null && this.parentSchema != null) { 72 JsonNode errorCodeNode = this.parentSchema.getSchemaNode().get(errorCodeKey); 73 if (errorCodeNode != null && errorCodeNode.isTextual()) { 74 String errorCodeText = errorCodeNode.asText(); 75 if (StringUtils.isNotBlank(errorCodeText)) { 76 this.errorMessageType = CustomErrorMessageType.of(errorCodeText); 77 } 78 } 79 } 80 } 81 82 protected void updateValidatorType(ValidatorTypeCode validatorTypeCode) { 83 updateKeyword(validatorTypeCode); 84 updateErrorMessageType(validatorTypeCode); 85 } 86 87 protected void updateErrorMessageType(ErrorMessageType errorMessageType) { 88 this.errorMessageType = errorMessageType; 89 } 90 91 protected void updateKeyword(Keyword keyword) { 92 this.keyword = keyword; 93 if (this.keyword != null) { 94 if (this.customErrorMessagesEnabled && keyword != null && parentSchema != null) { 95 this.errorMessage = getErrorMessage(parentSchema.getSchemaNode(), keyword.getValue()); 96 } 97 parseErrorCode(getErrorCodeKey(keyword.getValue())); 98 } 99 } 100 101 /** 102 * Gets the custom error message to use. 103 * 104 * @param schemaNode the schema node 105 * @param keyword the keyword 106 * @return the custom error message 107 */ 108 protected Map<String, String> getErrorMessage(JsonNode schemaNode, String keyword) { 109 final JsonSchema parentSchema = this.parentSchema; 110 final JsonNode message = getMessageNode(schemaNode, parentSchema, keyword); 111 if (message != null) { 112 JsonNode messageNode = message.get(keyword); 113 if (messageNode != null) { 114 if (messageNode.isTextual()) { 115 return Collections.singletonMap("", messageNode.asText()); 116 } else if (messageNode.isObject()) { 117 Map<String, String> result = new LinkedHashMap<>(); 118 messageNode.fields().forEachRemaining(entry -> { 119 result.put(entry.getKey(), entry.getValue().textValue()); 120 }); 121 if (!result.isEmpty()) { 122 return result; 123 } 124 } 125 } 126 } 127 return Collections.emptyMap(); 128 } 129 130 protected JsonNode getMessageNode(JsonNode schemaNode, JsonSchema parentSchema, String pname) { 131 if (schemaNode.get("message") != null && schemaNode.get("message").get(pname) != null) { 132 return schemaNode.get("message"); 133 } 134 JsonNode messageNode; 135 messageNode = schemaNode.get("message"); 136 if (messageNode == null && parentSchema != null) { 137 messageNode = parentSchema.schemaNode.get("message"); 138 if (messageNode == null) { 139 return getMessageNode(parentSchema.schemaNode, parentSchema.getParentSchema(), pname); 140 } 141 } 142 return messageNode; 143 } 144 145 protected String getErrorCodeKey(String keyword) { 146 if (keyword != null) { 147 return keyword + "ErrorCode"; 148 } 149 return null; 150 } 151 } 152