mirror of
https://gitflic.ru/project/openide/openide.git
synced 2025-12-15 02:59:33 +07:00
[json] IJPL-63554 Implemented fast exit for json schema validators
- If requested, validation will stop as soon as any error is found. This is extremelly important performance optimisation that plays well with the recenty introduced if-else branch computation. The number of calls to JsonSchemaResolver.isCorrect() increased dramatically, even more json-schema subsystem refactoring was demanded. The existing API didn't assume any kind of laziness or cancellability. The refactoring is performed in a way to cause minimal number of changes in code and API. It'd be great to rewrite the entire validation code to sequence/analogs once and drop complicated JsonAnnotationsCollectionMode GitOrigin-RevId: 4e62f7db76ed6b4071accbe1b80151c4b4664342
This commit is contained in:
committed by
intellij-monorepo-bot
parent
51749309f5
commit
23fb60afd8
@@ -0,0 +1,6 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.jetbrains.jsonSchema.extension
|
||||
|
||||
internal enum class JsonAnnotationsCollectionMode {
|
||||
FIND_ALL, FIND_FIRST
|
||||
}
|
||||
@@ -9,9 +9,16 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public interface JsonSchemaValidation {
|
||||
void validate(@NotNull JsonValueAdapter propValue,
|
||||
@NotNull JsonSchemaObject schema,
|
||||
@Nullable JsonSchemaType schemaType,
|
||||
@NotNull JsonValidationHost consumer,
|
||||
@NotNull JsonComplianceCheckerOptions options);
|
||||
/**
|
||||
* Validates given property adapter against given JSON-schema node considering the validation options. Results are recorded by the provided consumer instance.
|
||||
*
|
||||
* @return FALSE if the inspected propValue has errors, TRUE if the propValue is valid.
|
||||
* The implementations might consider returning the value as soon as the first error is found, or continue processing all the possible errors.
|
||||
* This behaviour is controlled by the {@link JsonComplianceCheckerOptions#shouldStopValidationAfterAnyErrorFound()} method.
|
||||
*/
|
||||
boolean validate(@NotNull JsonValueAdapter propValue,
|
||||
@NotNull JsonSchemaObject schema,
|
||||
@Nullable JsonSchemaType schemaType,
|
||||
@NotNull JsonValidationHost consumer,
|
||||
@NotNull JsonComplianceCheckerOptions options);
|
||||
}
|
||||
|
||||
@@ -7,6 +7,8 @@ import com.jetbrains.jsonSchema.impl.*;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public interface JsonValidationHost {
|
||||
void error(final String error, final PsiElement holder, JsonErrorPriority priority);
|
||||
void error(final PsiElement newHolder, JsonValidationError error);
|
||||
@@ -29,4 +31,6 @@ public interface JsonValidationHost {
|
||||
void addErrorsFrom(JsonValidationHost otherHost);
|
||||
|
||||
boolean hasRecordedErrorsFor(@NotNull JsonValueAdapter inspectedValueAdapter);
|
||||
|
||||
@NotNull Map<PsiElement, JsonValidationError> getErrors();
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.jetbrains.jsonSchema.impl;
|
||||
|
||||
import com.jetbrains.jsonSchema.extension.JsonAnnotationsCollectionMode;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public final class JsonComplianceCheckerOptions {
|
||||
public static final JsonComplianceCheckerOptions RELAX_ENUM_CHECK = new JsonComplianceCheckerOptions(true, false);
|
||||
|
||||
@@ -8,6 +11,7 @@ public final class JsonComplianceCheckerOptions {
|
||||
private final boolean isForceStrict;
|
||||
|
||||
private final boolean isReportMissingOptionalProperties;
|
||||
private final JsonAnnotationsCollectionMode errorsCollectionMode;
|
||||
|
||||
public JsonComplianceCheckerOptions(boolean caseInsensitiveEnumCheck) {
|
||||
this(caseInsensitiveEnumCheck, false);
|
||||
@@ -20,6 +24,14 @@ public final class JsonComplianceCheckerOptions {
|
||||
public JsonComplianceCheckerOptions(boolean isCaseInsensitiveEnumCheck,
|
||||
boolean isForceStrict,
|
||||
boolean isReportMissingOptionalProperties) {
|
||||
this(isCaseInsensitiveEnumCheck, isForceStrict, isReportMissingOptionalProperties, JsonAnnotationsCollectionMode.FIND_ALL);
|
||||
}
|
||||
|
||||
public JsonComplianceCheckerOptions(boolean isCaseInsensitiveEnumCheck,
|
||||
boolean isForceStrict,
|
||||
boolean isReportMissingOptionalProperties,
|
||||
@NotNull JsonAnnotationsCollectionMode errorsCollectionMode) {
|
||||
this.errorsCollectionMode = errorsCollectionMode;
|
||||
this.isCaseInsensitiveEnumCheck = isCaseInsensitiveEnumCheck;
|
||||
this.isForceStrict = isForceStrict;
|
||||
this.isReportMissingOptionalProperties = isReportMissingOptionalProperties;
|
||||
@@ -40,4 +52,8 @@ public final class JsonComplianceCheckerOptions {
|
||||
public boolean isReportMissingOptionalProperties() {
|
||||
return isReportMissingOptionalProperties;
|
||||
}
|
||||
|
||||
public boolean shouldStopValidationAfterAnyErrorFound() {
|
||||
return JsonAnnotationsCollectionMode.FIND_FIRST.equals(errorsCollectionMode);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,7 +42,8 @@ public final class JsonSchemaAnnotatorChecker implements JsonValidationHost {
|
||||
myErrors = new HashMap<>();
|
||||
}
|
||||
|
||||
public Map<PsiElement, JsonValidationError> getErrors() {
|
||||
@Override
|
||||
public @NotNull Map<PsiElement, JsonValidationError> getErrors() {
|
||||
return myErrors;
|
||||
}
|
||||
|
||||
@@ -165,14 +166,18 @@ public final class JsonSchemaAnnotatorChecker implements JsonValidationHost {
|
||||
|
||||
@Override
|
||||
public boolean isValid() {
|
||||
return myErrors.size() == 0 && !myHadTypeError;
|
||||
return myErrors.isEmpty() && !myHadTypeError;
|
||||
}
|
||||
|
||||
public void checkByScheme(@NotNull JsonValueAdapter value, @NotNull JsonSchemaObject schema) {
|
||||
public boolean checkByScheme(@NotNull JsonValueAdapter value, @NotNull JsonSchemaObject schema) {
|
||||
final JsonSchemaType instanceFieldType = JsonSchemaType.getType(value);
|
||||
|
||||
var isValid = true;
|
||||
for (JsonSchemaValidation validation : schema.getValidations(instanceFieldType, value)) {
|
||||
validation.validate(value, schema, instanceFieldType, this, myOptions);
|
||||
isValid = validation.validate(value, schema, instanceFieldType, this, myOptions);
|
||||
if (!isValid && myOptions.shouldStopValidationAfterAnyErrorFound()) return false;
|
||||
}
|
||||
return isValid;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -5,6 +5,7 @@ import com.intellij.openapi.util.Key
|
||||
import com.intellij.psi.util.CachedValue
|
||||
import com.intellij.psi.util.CachedValueProvider
|
||||
import com.intellij.psi.util.CachedValuesManager
|
||||
import com.jetbrains.jsonSchema.extension.JsonAnnotationsCollectionMode
|
||||
import com.jetbrains.jsonSchema.extension.adapters.JsonValueAdapter
|
||||
import com.jetbrains.jsonSchema.ide.JsonSchemaService
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
@@ -32,7 +33,13 @@ internal fun getOrComputeAdapterValidityAgainstGivenSchema(value: JsonValueAdapt
|
||||
return cachedValue
|
||||
}
|
||||
|
||||
val checker = JsonSchemaAnnotatorChecker(value.delegate.project, JsonComplianceCheckerOptions.RELAX_ENUM_CHECK)
|
||||
val checker = JsonSchemaAnnotatorChecker(
|
||||
value.delegate.project,
|
||||
JsonComplianceCheckerOptions(false,
|
||||
false,
|
||||
false,
|
||||
JsonAnnotationsCollectionMode.FIND_FIRST)
|
||||
)
|
||||
checker.checkByScheme(value, schema)
|
||||
val computedValue = checker.isCorrect
|
||||
|
||||
|
||||
@@ -7,62 +7,67 @@ import com.jetbrains.jsonSchema.extension.JsonValidationHost
|
||||
import com.jetbrains.jsonSchema.extension.adapters.JsonValueAdapter
|
||||
import com.jetbrains.jsonSchema.impl.JsonComplianceCheckerOptions
|
||||
import com.jetbrains.jsonSchema.impl.JsonSchemaObject
|
||||
import com.jetbrains.jsonSchema.impl.JsonSchemaType
|
||||
import com.jetbrains.jsonSchema.impl.validations.ArrayValidation
|
||||
import org.jetbrains.annotations.Nls
|
||||
|
||||
internal object Array2020Validator : ArrayValidation() {
|
||||
override fun validate(valueAdapter: JsonValueAdapter, schema: JsonSchemaObject, schemaType: JsonSchemaType?, consumer: JsonValidationHost, options: JsonComplianceCheckerOptions) {
|
||||
val arrayItems = valueAdapter.getAsArray()?.elements ?: return
|
||||
|
||||
validateUniqueItems(valueAdapter, arrayItems, schema, consumer)
|
||||
validateAgainstContainsSchema(valueAdapter, arrayItems, schema, consumer, options)
|
||||
validateIndividualItems(arrayItems, schema, consumer)
|
||||
validateArrayLength(valueAdapter, arrayItems, schema, consumer)
|
||||
validateArrayLengthHeuristically(valueAdapter, arrayItems, schema, consumer)
|
||||
}
|
||||
|
||||
private fun validateIndividualItems(instanceArrayItems: List<JsonValueAdapter>, schema: JsonSchemaObject, consumer: JsonValidationHost) {
|
||||
override fun validateIndividualItems(instanceArrayItems: List<JsonValueAdapter>, schema: JsonSchemaObject, consumer: JsonValidationHost, options: JsonComplianceCheckerOptions): Boolean {
|
||||
val additionalItemsSchemaList = schema.itemsSchemaList
|
||||
val firstRegularItemIndex = if (additionalItemsSchemaList.isNullOrEmpty()) 0 else additionalItemsSchemaList.size
|
||||
|
||||
var isValid = true
|
||||
|
||||
// check instance items with positional schema
|
||||
for (index in 0 until firstRegularItemIndex) {
|
||||
val positionalSchema = additionalItemsSchemaList?.get(index) ?: break
|
||||
val inspectedInstanceItem = instanceArrayItems.getOrNull(index) ?: break
|
||||
consumer.checkObjectBySchemaRecordErrors(positionalSchema, inspectedInstanceItem)
|
||||
|
||||
isValid = isValid && consumer.errors.isEmpty()
|
||||
if (!isValid && options.shouldStopValidationAfterAnyErrorFound()) return false
|
||||
}
|
||||
|
||||
// check the rest of instance items with regular schema
|
||||
val additionalItemsSchema = schema.additionalItemsSchema
|
||||
if (additionalItemsSchema != null) {
|
||||
validateAgainstNonPositionalSchema(additionalItemsSchema, instanceArrayItems, firstRegularItemIndex, consumer, JsonBundle.message("schema.validation.array.no.extra"))
|
||||
return
|
||||
isValid = isValid && validateAgainstNonPositionalSchema(additionalItemsSchema, instanceArrayItems, firstRegularItemIndex, consumer, options, JsonBundle.message("schema.validation.array.no.extra"))
|
||||
if (!isValid && options.shouldStopValidationAfterAnyErrorFound()) return false
|
||||
}
|
||||
|
||||
val unevaluatedItemsSchema = schema.unevaluatedItemsSchema
|
||||
if (unevaluatedItemsSchema != null) {
|
||||
validateAgainstNonPositionalSchema(unevaluatedItemsSchema, instanceArrayItems, firstRegularItemIndex, consumer, JsonBundle.message("schema.validation.array.no.unevaluated"))
|
||||
isValid = isValid && validateAgainstNonPositionalSchema(unevaluatedItemsSchema, instanceArrayItems, firstRegularItemIndex, consumer, options, JsonBundle.message("schema.validation.array.no.unevaluated"))
|
||||
if (!isValid && options.shouldStopValidationAfterAnyErrorFound()) return false
|
||||
}
|
||||
|
||||
return isValid
|
||||
}
|
||||
|
||||
private fun validateAgainstNonPositionalSchema(nonPositionalItemsSchema: JsonSchemaObject,
|
||||
instanceArrayItems: List<JsonValueAdapter>,
|
||||
firstRegularItemIndex: Int,
|
||||
consumer: JsonValidationHost,
|
||||
errorMessage: @Nls String) {
|
||||
private fun validateAgainstNonPositionalSchema(
|
||||
nonPositionalItemsSchema: JsonSchemaObject,
|
||||
instanceArrayItems: List<JsonValueAdapter>,
|
||||
firstRegularItemIndex: Int,
|
||||
consumer: JsonValidationHost,
|
||||
options: JsonComplianceCheckerOptions,
|
||||
errorMessage: @Nls String,
|
||||
): Boolean {
|
||||
if (nonPositionalItemsSchema.constantSchema == true) {
|
||||
return
|
||||
return true
|
||||
}
|
||||
|
||||
if (nonPositionalItemsSchema.constantSchema == false && instanceArrayItems.getOrNull(firstRegularItemIndex) != null) {
|
||||
consumer.error(errorMessage, instanceArrayItems[firstRegularItemIndex].delegate, JsonErrorPriority.LOW_PRIORITY)
|
||||
return
|
||||
return false
|
||||
}
|
||||
|
||||
var isValid = true
|
||||
for (index in firstRegularItemIndex until instanceArrayItems.size) {
|
||||
val instanceArrayItem = instanceArrayItems.getOrNull(index) ?: break
|
||||
consumer.checkObjectBySchemaRecordErrors(nonPositionalItemsSchema, instanceArrayItem)
|
||||
|
||||
isValid = isValid && consumer.errors.isEmpty()
|
||||
if (!isValid && options.shouldStopValidationAfterAnyErrorFound()) return false
|
||||
}
|
||||
return isValid
|
||||
}
|
||||
}
|
||||
@@ -22,40 +22,56 @@ import java.util.Map;
|
||||
public class ArrayValidation implements JsonSchemaValidation {
|
||||
public static final ArrayValidation INSTANCE = new ArrayValidation();
|
||||
@Override
|
||||
public void validate(@NotNull JsonValueAdapter propValue,
|
||||
@NotNull JsonSchemaObject schema,
|
||||
@Nullable JsonSchemaType schemaType,
|
||||
@NotNull JsonValidationHost consumer,
|
||||
@NotNull JsonComplianceCheckerOptions options) {
|
||||
checkArray(propValue, schema, consumer, options);
|
||||
public boolean validate(@NotNull JsonValueAdapter propValue,
|
||||
@NotNull JsonSchemaObject schema,
|
||||
@Nullable JsonSchemaType schemaType,
|
||||
@NotNull JsonValidationHost consumer,
|
||||
@NotNull JsonComplianceCheckerOptions options) {
|
||||
return checkArray(propValue, schema, consumer, options);
|
||||
}
|
||||
|
||||
private static void checkArray(JsonValueAdapter value,
|
||||
private boolean checkArray(JsonValueAdapter value,
|
||||
JsonSchemaObject schema,
|
||||
JsonValidationHost consumer,
|
||||
JsonComplianceCheckerOptions options) {
|
||||
final JsonArrayValueAdapter asArray = value.getAsArray();
|
||||
if (asArray == null) return;
|
||||
if (asArray == null) return true;
|
||||
final List<JsonValueAdapter> elements = asArray.getElements();
|
||||
checkArrayItems(value, elements, schema, consumer, options);
|
||||
return checkArrayItems(value, elements, schema, consumer, options);
|
||||
}
|
||||
|
||||
private static void checkArrayItems(@NotNull JsonValueAdapter array,
|
||||
final @NotNull List<JsonValueAdapter> list,
|
||||
final JsonSchemaObject schema,
|
||||
JsonValidationHost consumer,
|
||||
JsonComplianceCheckerOptions options) {
|
||||
validateUniqueItems(array, list, schema, consumer);
|
||||
validateAgainstContainsSchema(array, list, schema, consumer, options);
|
||||
validateIndividualItems(list, schema, consumer);
|
||||
validateArrayLength(array, list, schema, consumer);
|
||||
validateArrayLengthHeuristically(array, list, schema, consumer);
|
||||
protected boolean checkArrayItems(@NotNull JsonValueAdapter array,
|
||||
final @NotNull List<JsonValueAdapter> list,
|
||||
final JsonSchemaObject schema,
|
||||
JsonValidationHost consumer,
|
||||
JsonComplianceCheckerOptions options) {
|
||||
if (options.shouldStopValidationAfterAnyErrorFound()) {
|
||||
return validateUniqueItems(array, list, schema, consumer, options) &&
|
||||
validateAgainstContainsSchema(array, list, schema, consumer, options) &&
|
||||
validateIndividualItems(list, schema, consumer, options) &&
|
||||
validateArrayLength(array, list, schema, consumer, options) &&
|
||||
validateArrayLengthHeuristically(array, list, schema, consumer, options);
|
||||
}
|
||||
else {
|
||||
return validateUniqueItems(array, list, schema, consumer, options) &
|
||||
validateAgainstContainsSchema(array, list, schema, consumer, options) &
|
||||
validateIndividualItems(list, schema, consumer, options) &
|
||||
validateArrayLength(array, list, schema, consumer, options) &
|
||||
validateArrayLengthHeuristically(array, list, schema, consumer, options);
|
||||
}
|
||||
}
|
||||
|
||||
private static void validateIndividualItems(@NotNull List<JsonValueAdapter> list, JsonSchemaObject schema, JsonValidationHost consumer) {
|
||||
protected boolean validateIndividualItems(@NotNull List<JsonValueAdapter> list,
|
||||
JsonSchemaObject schema,
|
||||
JsonValidationHost consumer,
|
||||
JsonComplianceCheckerOptions options) {
|
||||
var isValid = true;
|
||||
|
||||
if (schema.getItemsSchema() != null) {
|
||||
for (JsonValueAdapter item : list) {
|
||||
consumer.checkObjectBySchemaRecordErrors(schema.getItemsSchema(), item);
|
||||
isValid &= consumer.getErrors().isEmpty();
|
||||
if (!isValid && options.shouldStopValidationAfterAnyErrorFound()) return false;
|
||||
}
|
||||
}
|
||||
else if (schema.getItemsSchemaList() != null) {
|
||||
@@ -63,45 +79,60 @@ public class ArrayValidation implements JsonSchemaValidation {
|
||||
for (JsonValueAdapter arrayValue : list) {
|
||||
if (iterator.hasNext()) {
|
||||
consumer.checkObjectBySchemaRecordErrors(iterator.next(), arrayValue);
|
||||
isValid &= consumer.getErrors().isEmpty();
|
||||
if (!isValid && options.shouldStopValidationAfterAnyErrorFound()) return false;
|
||||
}
|
||||
else {
|
||||
if (!Boolean.TRUE.equals(schema.getAdditionalItemsAllowed())) {
|
||||
consumer.error(JsonBundle.message("schema.validation.array.no.extra"), arrayValue.getDelegate(), JsonErrorPriority.LOW_PRIORITY);
|
||||
isValid = false;
|
||||
if (options.shouldStopValidationAfterAnyErrorFound()) return false;
|
||||
}
|
||||
else if (schema.getAdditionalItemsSchema() != null) {
|
||||
consumer.checkObjectBySchemaRecordErrors(schema.getAdditionalItemsSchema(), arrayValue);
|
||||
isValid &= consumer.getErrors().isEmpty();
|
||||
if (!isValid && options.shouldStopValidationAfterAnyErrorFound()) return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return isValid;
|
||||
}
|
||||
|
||||
protected static void validateArrayLengthHeuristically(@NotNull JsonValueAdapter array,
|
||||
@NotNull List<JsonValueAdapter> list,
|
||||
JsonSchemaObject schema,
|
||||
JsonValidationHost consumer) {
|
||||
protected static boolean validateArrayLengthHeuristically(@NotNull JsonValueAdapter array,
|
||||
@NotNull List<JsonValueAdapter> list,
|
||||
JsonSchemaObject schema,
|
||||
JsonValidationHost consumer,
|
||||
JsonComplianceCheckerOptions options) {
|
||||
// these two are not correct by the schema spec, but are used in some schemas
|
||||
if (schema.getMinLength() != null && list.size() < schema.getMinLength()) {
|
||||
consumer.error(JsonBundle.message("schema.validation.array.shorter.than", schema.getMinLength()), array.getDelegate(), JsonErrorPriority.LOW_PRIORITY);
|
||||
return false;
|
||||
}
|
||||
if (schema.getMaxLength() != null && list.size() > schema.getMaxLength()) {
|
||||
consumer.error(JsonBundle.message("schema.validation.array.longer.than", schema.getMaxLength()), array.getDelegate(), JsonErrorPriority.LOW_PRIORITY);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected static void validateArrayLength(@NotNull JsonValueAdapter array,
|
||||
@NotNull List<JsonValueAdapter> list,
|
||||
JsonSchemaObject schema,
|
||||
JsonValidationHost consumer) {
|
||||
protected static boolean validateArrayLength(@NotNull JsonValueAdapter array,
|
||||
@NotNull List<JsonValueAdapter> list,
|
||||
JsonSchemaObject schema,
|
||||
JsonValidationHost consumer, JsonComplianceCheckerOptions options) {
|
||||
if (schema.getMinItems() != null && list.size() < schema.getMinItems()) {
|
||||
consumer.error(JsonBundle.message("schema.validation.array.shorter.than", schema.getMinItems()), array.getDelegate(), JsonErrorPriority.LOW_PRIORITY);
|
||||
return false;
|
||||
}
|
||||
if (schema.getMaxItems() != null && list.size() > schema.getMaxItems()) {
|
||||
consumer.error(JsonBundle.message("schema.validation.array.longer.than", schema.getMaxItems()), array.getDelegate(), JsonErrorPriority.LOW_PRIORITY);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected static void validateAgainstContainsSchema(@NotNull JsonValueAdapter array,
|
||||
protected static boolean validateAgainstContainsSchema(@NotNull JsonValueAdapter array,
|
||||
@NotNull List<JsonValueAdapter> list,
|
||||
JsonSchemaObject schema,
|
||||
JsonValidationHost consumer,
|
||||
@@ -117,14 +148,17 @@ public class ArrayValidation implements JsonSchemaValidation {
|
||||
}
|
||||
if (!match) {
|
||||
consumer.error(JsonBundle.message("schema.validation.array.not.contains"), array.getDelegate(), JsonErrorPriority.MEDIUM_PRIORITY);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected static void validateUniqueItems(@NotNull JsonValueAdapter array,
|
||||
protected static boolean validateUniqueItems(@NotNull JsonValueAdapter array,
|
||||
@NotNull List<JsonValueAdapter> list,
|
||||
JsonSchemaObject schema,
|
||||
JsonValidationHost consumer) {
|
||||
JsonValidationHost consumer,
|
||||
@NotNull JsonComplianceCheckerOptions options) {
|
||||
if (schema.isUniqueItems()) {
|
||||
final MultiMap<String, JsonValueAdapter> valueTexts = new MultiMap<>();
|
||||
final JsonLikePsiWalker walker = JsonLikePsiWalker.getWalker(array.getDelegate(), schema);
|
||||
@@ -138,9 +172,11 @@ public class ArrayValidation implements JsonSchemaValidation {
|
||||
for (JsonValueAdapter item: entry.getValue()) {
|
||||
if (!item.shouldCheckAsValue()) continue;
|
||||
consumer.error(JsonBundle.message("schema.validation.not.unique"), item.getDelegate(), JsonErrorPriority.TYPE_MISMATCH);
|
||||
if (options.shouldStopValidationAfterAnyErrorFound()) return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,9 +11,11 @@ import com.jetbrains.jsonSchema.impl.JsonSchemaObject
|
||||
import com.jetbrains.jsonSchema.impl.JsonSchemaType
|
||||
|
||||
internal object ConstantSchemaValidation: JsonSchemaValidation {
|
||||
override fun validate(propValue: JsonValueAdapter, schema: JsonSchemaObject, schemaType: JsonSchemaType?, consumer: JsonValidationHost, options: JsonComplianceCheckerOptions) {
|
||||
override fun validate(propValue: JsonValueAdapter, schema: JsonSchemaObject, schemaType: JsonSchemaType?, consumer: JsonValidationHost, options: JsonComplianceCheckerOptions): Boolean {
|
||||
if (schema.constantSchema == false) {
|
||||
consumer.error(JsonBundle.message("schema.validation.constant.schema"), propValue.delegate.parent, JsonErrorPriority.LOW_PRIORITY)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -24,25 +24,26 @@ import static com.jetbrains.jsonSchema.impl.light.SchemaKeywordsKt.X_INTELLIJ_CA
|
||||
public final class EnumValidation implements JsonSchemaValidation {
|
||||
public static final EnumValidation INSTANCE = new EnumValidation();
|
||||
@Override
|
||||
public void validate(@NotNull JsonValueAdapter propValue,
|
||||
@NotNull JsonSchemaObject schema,
|
||||
@Nullable JsonSchemaType schemaType,
|
||||
@NotNull JsonValidationHost consumer,
|
||||
@NotNull JsonComplianceCheckerOptions options) {
|
||||
public boolean validate(@NotNull JsonValueAdapter propValue,
|
||||
@NotNull JsonSchemaObject schema,
|
||||
@Nullable JsonSchemaType schemaType,
|
||||
@NotNull JsonValidationHost consumer,
|
||||
@NotNull JsonComplianceCheckerOptions options) {
|
||||
List<Object> enumItems = schema.getEnum();
|
||||
if (enumItems == null) return;
|
||||
if (enumItems == null) return true;
|
||||
final JsonLikePsiWalker walker = JsonLikePsiWalker.getWalker(propValue.getDelegate(), schema);
|
||||
if (walker == null) return;
|
||||
if (walker == null) return true;
|
||||
final String text = StringUtil.notNullize(walker.getNodeTextForValidation(propValue.getDelegate()));
|
||||
boolean caseInsensitive = Boolean.parseBoolean(schema.readChildNodeValue(X_INTELLIJ_CASE_INSENSITIVE)) || schema.isForceCaseInsensitive();
|
||||
BiFunction<String, String, Boolean> eq = options.isCaseInsensitiveEnumCheck() || caseInsensitive
|
||||
? String::equalsIgnoreCase
|
||||
: String::equals;
|
||||
for (Object object : enumItems) {
|
||||
if (checkEnumValue(object, walker, propValue, text, eq)) return;
|
||||
if (checkEnumValue(object, walker, propValue, text, eq)) return true;
|
||||
}
|
||||
consumer.error(JsonBundle.message("schema.validation.enum.mismatch", StringUtil.join(enumItems, o -> o.toString(), ", ")), propValue.getDelegate(),
|
||||
JsonValidationError.FixableIssueKind.NonEnumValue, null, JsonErrorPriority.MEDIUM_PRIORITY);
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean checkEnumValue(@NotNull Object object,
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
package com.jetbrains.jsonSchema.impl.validations;
|
||||
|
||||
import com.intellij.json.JsonBundle;
|
||||
import com.jetbrains.jsonSchema.extension.JsonAnnotationsCollectionMode;
|
||||
import com.jetbrains.jsonSchema.extension.JsonErrorPriority;
|
||||
import com.jetbrains.jsonSchema.extension.JsonSchemaValidation;
|
||||
import com.jetbrains.jsonSchema.extension.JsonValidationHost;
|
||||
@@ -18,20 +19,29 @@ import java.util.Collection;
|
||||
public final class NotValidation implements JsonSchemaValidation {
|
||||
public static final NotValidation INSTANCE = new NotValidation();
|
||||
@Override
|
||||
public void validate(@NotNull JsonValueAdapter propValue,
|
||||
@NotNull JsonSchemaObject schema,
|
||||
@Nullable JsonSchemaType schemaType,
|
||||
@NotNull JsonValidationHost consumer,
|
||||
@NotNull JsonComplianceCheckerOptions options) {
|
||||
public boolean validate(@NotNull JsonValueAdapter propValue,
|
||||
@NotNull JsonSchemaObject schema,
|
||||
@Nullable JsonSchemaType schemaType,
|
||||
@NotNull JsonValidationHost consumer,
|
||||
@NotNull JsonComplianceCheckerOptions options) {
|
||||
final MatchResult result = consumer.resolve(schema.getNot(), propValue);
|
||||
if (result.mySchemas.isEmpty() && result.myExcludingSchemas.isEmpty()) return;
|
||||
if (result.mySchemas.isEmpty() && result.myExcludingSchemas.isEmpty()) return true;
|
||||
|
||||
// if 'not' uses reference to owning schema back -> do not check, seems it does not make any sense
|
||||
if (result.mySchemas.stream().anyMatch(s -> schema.equals(s)) ||
|
||||
result.myExcludingSchemas.stream().flatMap(Collection::stream)
|
||||
.anyMatch(s -> schema.equals(s))) return;
|
||||
.anyMatch(s -> schema.equals(s))) return true;
|
||||
|
||||
final JsonValidationHost checker = consumer.checkByMatchResult(propValue, result, options.withForcedStrict());
|
||||
if (checker == null || checker.isValid()) consumer.error(JsonBundle.message("schema.validation.against.not"), propValue.getDelegate(), JsonErrorPriority.NOT_SCHEMA);
|
||||
final JsonValidationHost checker =
|
||||
consumer.checkByMatchResult(propValue,
|
||||
result,
|
||||
new JsonComplianceCheckerOptions(options.isCaseInsensitiveEnumCheck(), true, false,
|
||||
JsonAnnotationsCollectionMode.FIND_FIRST));
|
||||
if (checker == null || checker.isValid()) {
|
||||
consumer.error(JsonBundle.message("schema.validation.against.not"), propValue.getDelegate(), JsonErrorPriority.NOT_SCHEMA);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,20 +13,21 @@ import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public final class NumericValidation implements JsonSchemaValidation {
|
||||
public static final NumericValidation INSTANCE = new NumericValidation();
|
||||
private static void checkNumber(PsiElement propValue,
|
||||
JsonSchemaObject schema,
|
||||
JsonSchemaType schemaType,
|
||||
JsonValidationHost consumer) {
|
||||
private static boolean checkNumber(PsiElement propValue,
|
||||
JsonSchemaObject schema,
|
||||
JsonSchemaType schemaType,
|
||||
JsonValidationHost consumer,
|
||||
@NotNull JsonComplianceCheckerOptions options) {
|
||||
Number value;
|
||||
String valueText = JsonSchemaAnnotatorChecker.getValue(propValue, schema);
|
||||
if (valueText == null) return;
|
||||
if (valueText == null) return true;
|
||||
if (JsonSchemaType._integer.equals(schemaType)) {
|
||||
value = JsonSchemaType.getIntegerValue(valueText);
|
||||
if (value == null) {
|
||||
consumer.error(JsonBundle.message("schema.validation.integer.expected"), propValue,
|
||||
JsonValidationError.FixableIssueKind.TypeMismatch,
|
||||
new JsonValidationError.TypeMismatchIssueData(new JsonSchemaType[]{schemaType}), JsonErrorPriority.TYPE_MISMATCH);
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -38,8 +39,9 @@ public final class NumericValidation implements JsonSchemaValidation {
|
||||
consumer.error(JsonBundle.message("schema.validation.number.expected"), propValue,
|
||||
JsonValidationError.FixableIssueKind.TypeMismatch,
|
||||
new JsonValidationError.TypeMismatchIssueData(new JsonSchemaType[]{schemaType}), JsonErrorPriority.TYPE_MISMATCH);
|
||||
return false;
|
||||
}
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
final Number multipleOf = schema.getMultipleOf();
|
||||
@@ -49,77 +51,95 @@ public final class NumericValidation implements JsonSchemaValidation {
|
||||
final String multipleOfValue = String.valueOf(Math.abs(multipleOf.doubleValue() - multipleOf.intValue()) < 0.000001 ?
|
||||
multipleOf.intValue() : multipleOf);
|
||||
consumer.error(JsonBundle.message("schema.validation.not.multiple.of", multipleOfValue), propValue, JsonErrorPriority.LOW_PRIORITY);
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
checkMinimum(schema, value, propValue, consumer);
|
||||
checkMaximum(schema, value, propValue, consumer);
|
||||
return checkMinimum(schema, value, propValue, consumer, options) &
|
||||
checkMaximum(schema, value, propValue, consumer, options);
|
||||
}
|
||||
|
||||
private static void checkMaximum(JsonSchemaObject schema,
|
||||
private static boolean checkMaximum(JsonSchemaObject schema,
|
||||
Number value,
|
||||
PsiElement propertyValue,
|
||||
JsonValidationHost consumer) {
|
||||
|
||||
JsonValidationHost consumer,
|
||||
@NotNull JsonComplianceCheckerOptions options) {
|
||||
var isValid = true;
|
||||
Number exclusiveMaximumNumber = schema.getExclusiveMaximumNumber();
|
||||
if (exclusiveMaximumNumber != null) {
|
||||
final double doubleValue = exclusiveMaximumNumber.doubleValue();
|
||||
if (value.doubleValue() >= doubleValue) {
|
||||
consumer.error(JsonBundle.message("schema.validation.greater.than.exclusive.maximum", exclusiveMaximumNumber), propertyValue, JsonErrorPriority.LOW_PRIORITY);
|
||||
isValid = false;
|
||||
if (options.shouldStopValidationAfterAnyErrorFound()) return false;
|
||||
}
|
||||
}
|
||||
Number maximum = schema.getMaximum();
|
||||
if (maximum == null) return;
|
||||
if (maximum == null) return isValid;
|
||||
boolean isExclusive = Boolean.TRUE.equals(schema.isExclusiveMaximum());
|
||||
final double doubleValue = maximum.doubleValue();
|
||||
if (isExclusive) {
|
||||
if (value.doubleValue() >= doubleValue) {
|
||||
consumer.error(JsonBundle.message("schema.validation.greater.than.exclusive.maximum", maximum), propertyValue, JsonErrorPriority.LOW_PRIORITY);
|
||||
isValid = false;
|
||||
if (options.shouldStopValidationAfterAnyErrorFound()) return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (value.doubleValue() > doubleValue) {
|
||||
consumer.error(JsonBundle.message("schema.validation.greater.than.maximum", maximum), propertyValue, JsonErrorPriority.LOW_PRIORITY);
|
||||
isValid = false;
|
||||
if (options.shouldStopValidationAfterAnyErrorFound()) return false;
|
||||
}
|
||||
}
|
||||
|
||||
return isValid;
|
||||
}
|
||||
|
||||
private static void checkMinimum(JsonSchemaObject schema,
|
||||
Number value,
|
||||
PsiElement propertyValue,
|
||||
JsonValidationHost consumer) {
|
||||
private static boolean checkMinimum(JsonSchemaObject schema,
|
||||
Number value,
|
||||
PsiElement propertyValue,
|
||||
JsonValidationHost consumer, @NotNull JsonComplianceCheckerOptions options) {
|
||||
var isValid = true;
|
||||
// schema v6 - exclusiveMinimum is numeric now
|
||||
Number exclusiveMinimumNumber = schema.getExclusiveMinimumNumber();
|
||||
if (exclusiveMinimumNumber != null) {
|
||||
final double doubleValue = exclusiveMinimumNumber.doubleValue();
|
||||
if (value.doubleValue() <= doubleValue) {
|
||||
consumer.error(JsonBundle.message("schema.validation.less.than.exclusive.minimum", exclusiveMinimumNumber), propertyValue, JsonErrorPriority.LOW_PRIORITY);
|
||||
isValid = false;
|
||||
if (options.shouldStopValidationAfterAnyErrorFound()) return false;
|
||||
}
|
||||
}
|
||||
|
||||
Number minimum = schema.getMinimum();
|
||||
if (minimum == null) return;
|
||||
if (minimum == null) return isValid;
|
||||
boolean isExclusive = Boolean.TRUE.equals(schema.isExclusiveMinimum());
|
||||
final double doubleValue = minimum.doubleValue();
|
||||
if (isExclusive) {
|
||||
if (value.doubleValue() <= doubleValue) {
|
||||
consumer.error(JsonBundle.message("schema.validation.less.than.exclusive.minimum", minimum), propertyValue, JsonErrorPriority.LOW_PRIORITY);
|
||||
isValid = false;
|
||||
if (options.shouldStopValidationAfterAnyErrorFound()) return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (value.doubleValue() < doubleValue) {
|
||||
consumer.error(JsonBundle.message("schema.validation.less.than.minimum", minimum), propertyValue, JsonErrorPriority.LOW_PRIORITY);
|
||||
isValid = false;
|
||||
if (options.shouldStopValidationAfterAnyErrorFound()) return false;
|
||||
}
|
||||
}
|
||||
|
||||
return isValid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validate(@NotNull JsonValueAdapter propValue,
|
||||
@NotNull JsonSchemaObject schema,
|
||||
@Nullable JsonSchemaType schemaType,
|
||||
@NotNull JsonValidationHost consumer,
|
||||
@NotNull JsonComplianceCheckerOptions options) {
|
||||
checkNumber(propValue.getDelegate(), schema, schemaType, consumer);
|
||||
public boolean validate(@NotNull JsonValueAdapter propValue,
|
||||
@NotNull JsonSchemaObject schema,
|
||||
@Nullable JsonSchemaType schemaType,
|
||||
@NotNull JsonValidationHost consumer,
|
||||
@NotNull JsonComplianceCheckerOptions options) {
|
||||
return checkNumber(propValue.getDelegate(), schema, schemaType, consumer, options);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,19 +30,22 @@ public final class ObjectValidation implements JsonSchemaValidation {
|
||||
public static final ObjectValidation INSTANCE = new ObjectValidation();
|
||||
|
||||
@Override
|
||||
public void validate(@NotNull JsonValueAdapter propValue,
|
||||
@NotNull JsonSchemaObject schema,
|
||||
@Nullable JsonSchemaType schemaType,
|
||||
@NotNull JsonValidationHost consumer,
|
||||
@NotNull JsonComplianceCheckerOptions options) {
|
||||
checkObject(propValue, schema, consumer, options);
|
||||
public boolean validate(@NotNull JsonValueAdapter propValue,
|
||||
@NotNull JsonSchemaObject schema,
|
||||
@Nullable JsonSchemaType schemaType,
|
||||
@NotNull JsonValidationHost consumer,
|
||||
@NotNull JsonComplianceCheckerOptions options) {
|
||||
return checkObject(propValue, schema, consumer, options);
|
||||
}
|
||||
|
||||
private static void checkObject(@NotNull JsonValueAdapter value,
|
||||
@NotNull JsonSchemaObject schema,
|
||||
JsonValidationHost consumer, JsonComplianceCheckerOptions options) {
|
||||
private static boolean checkObject(@NotNull JsonValueAdapter value,
|
||||
@NotNull JsonSchemaObject schema,
|
||||
JsonValidationHost consumer,
|
||||
JsonComplianceCheckerOptions options) {
|
||||
final JsonObjectValueAdapter object = value.getAsObject();
|
||||
if (object == null) return;
|
||||
if (object == null) return true;
|
||||
|
||||
var isValid = true;
|
||||
|
||||
final List<JsonPropertyAdapter> propertyList = object.getPropertyList();
|
||||
final Set<String> set = new HashSet<>();
|
||||
@@ -56,6 +59,8 @@ public final class ObjectValidation implements JsonSchemaValidation {
|
||||
nameValueAdapter), options);
|
||||
if (checker != null) {
|
||||
consumer.addErrorsFrom(checker);
|
||||
isValid = false;
|
||||
if (options.shouldStopValidationAfterAnyErrorFound()) return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -66,10 +71,14 @@ public final class ObjectValidation implements JsonSchemaValidation {
|
||||
consumer.error(JsonBundle.message("json.schema.annotation.not.allowed.property", name), property.getDelegate(),
|
||||
JsonValidationError.FixableIssueKind.ProhibitedProperty,
|
||||
new JsonValidationError.ProhibitedPropertyIssueData(name), JsonErrorPriority.LOW_PRIORITY);
|
||||
isValid = false;
|
||||
if (options.shouldStopValidationAfterAnyErrorFound()) return false;
|
||||
}
|
||||
else if (ThreeState.UNSURE.equals(pair.getFirst()) && pair.second.getConstantSchema() == null) {
|
||||
for (JsonValueAdapter propertyValue : property.getValues()) {
|
||||
consumer.checkObjectBySchemaRecordErrors(pair.getSecond(), propertyValue);
|
||||
isValid &= consumer.getErrors().isEmpty();
|
||||
if (!isValid && options.shouldStopValidationAfterAnyErrorFound()) return false;
|
||||
}
|
||||
}
|
||||
set.add(name);
|
||||
@@ -86,15 +95,21 @@ public final class ObjectValidation implements JsonSchemaValidation {
|
||||
consumer.error(JsonBundle.message("schema.validation.missing.required.property.or.properties", data.getMessage(false)),
|
||||
value.getDelegate(), JsonValidationError.FixableIssueKind.MissingProperty, data,
|
||||
JsonErrorPriority.MISSING_PROPS);
|
||||
isValid = false;
|
||||
if (options.shouldStopValidationAfterAnyErrorFound()) return false;
|
||||
}
|
||||
}
|
||||
if (schema.getMinProperties() != null && propertyList.size() < schema.getMinProperties()) {
|
||||
consumer.error(JsonBundle.message("schema.validation.number.of.props.less.than", schema.getMinProperties()), value.getDelegate(),
|
||||
JsonErrorPriority.LOW_PRIORITY);
|
||||
isValid = false;
|
||||
if (options.shouldStopValidationAfterAnyErrorFound()) return false;
|
||||
}
|
||||
if (schema.getMaxProperties() != null && propertyList.size() > schema.getMaxProperties()) {
|
||||
consumer.error(JsonBundle.message("schema.validation.number.of.props.greater.than", schema.getMaxProperties()), value.getDelegate(),
|
||||
JsonErrorPriority.LOW_PRIORITY);
|
||||
isValid = false;
|
||||
if (options.shouldStopValidationAfterAnyErrorFound()) return false;
|
||||
}
|
||||
final Map<String, List<String>> dependencies = schema.getPropertyDependencies();
|
||||
if (dependencies != null) {
|
||||
@@ -110,32 +125,35 @@ public final class ObjectValidation implements JsonSchemaValidation {
|
||||
value.getDelegate(),
|
||||
JsonValidationError.FixableIssueKind.MissingProperty,
|
||||
data, JsonErrorPriority.MISSING_PROPS);
|
||||
isValid = false;
|
||||
if (options.shouldStopValidationAfterAnyErrorFound()) return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
final var schemaDependencies = schema.getSchemaDependencyNames();
|
||||
StreamEx.of(schemaDependencies)
|
||||
.forEach(name -> {
|
||||
var dependency = schema.getSchemaDependencyByName(name);
|
||||
if (set.contains(name) && dependency != null) {
|
||||
consumer.checkObjectBySchemaRecordErrors(dependency, value);
|
||||
}
|
||||
});
|
||||
|
||||
reportUnevaluatedPropertiesSchemaViolation(consumer, schema, object);
|
||||
for (String name : StreamEx.of(schema.getSchemaDependencyNames())) {
|
||||
var dependency = schema.getSchemaDependencyByName(name);
|
||||
if (set.contains(name) && dependency != null) {
|
||||
consumer.checkObjectBySchemaRecordErrors(dependency, value);
|
||||
isValid &= consumer.getErrors().isEmpty();
|
||||
if (!isValid && options.shouldStopValidationAfterAnyErrorFound()) return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return checkUnevaluatedPropertiesSchemaViolation(consumer, schema, object, options);
|
||||
}
|
||||
|
||||
private static void reportUnevaluatedPropertiesSchemaViolation(@NotNull JsonValidationHost consumer,
|
||||
@NotNull JsonSchemaObject schemaNode,
|
||||
@NotNull JsonObjectValueAdapter inspectedObject) {
|
||||
private static boolean checkUnevaluatedPropertiesSchemaViolation(@NotNull JsonValidationHost consumer,
|
||||
@NotNull JsonSchemaObject schemaNode,
|
||||
@NotNull JsonObjectValueAdapter inspectedObject,
|
||||
@NotNull JsonComplianceCheckerOptions options) {
|
||||
var unevaluatedPropertiesSchema = schemaNode.getUnevaluatedPropertiesSchema();
|
||||
if (unevaluatedPropertiesSchema == null) return;
|
||||
if (unevaluatedPropertiesSchema == null) return true;
|
||||
|
||||
var constantSchemaValue = unevaluatedPropertiesSchema.getConstantSchema();
|
||||
if (Boolean.TRUE.equals(constantSchemaValue)) return;
|
||||
if (Boolean.TRUE.equals(constantSchemaValue)) return true;
|
||||
|
||||
var isValid = true;
|
||||
for (JsonPropertyAdapter childPropertyAdapter : inspectedObject.getPropertyList()) {
|
||||
if (isCoveredByAdjacentSchemas(consumer, childPropertyAdapter, schemaNode)) {
|
||||
continue;
|
||||
@@ -145,7 +163,10 @@ public final class ObjectValidation implements JsonSchemaValidation {
|
||||
if (childPropertyNameAdapter == null) continue;
|
||||
|
||||
consumer.checkObjectBySchemaRecordErrors(unevaluatedPropertiesSchema, childPropertyNameAdapter);
|
||||
isValid &= consumer.getErrors().isEmpty();
|
||||
if (!isValid && options.shouldStopValidationAfterAnyErrorFound()) return false;
|
||||
}
|
||||
return isValid;
|
||||
}
|
||||
|
||||
private static boolean isCoveredByAdjacentSchemas(@NotNull JsonValidationHost validationHost,
|
||||
|
||||
@@ -19,45 +19,44 @@ import static com.jetbrains.jsonSchema.impl.JsonSchemaAnnotatorChecker.getValue;
|
||||
public final class StringValidation implements JsonSchemaValidation {
|
||||
public static final StringValidation INSTANCE = new StringValidation();
|
||||
@Override
|
||||
public void validate(@NotNull JsonValueAdapter propValue,
|
||||
@NotNull JsonSchemaObject schema,
|
||||
@Nullable JsonSchemaType schemaType,
|
||||
@NotNull JsonValidationHost consumer,
|
||||
@NotNull JsonComplianceCheckerOptions options) {
|
||||
checkString(propValue.getDelegate(), schema, consumer);
|
||||
public boolean validate(@NotNull JsonValueAdapter propValue,
|
||||
@NotNull JsonSchemaObject schema,
|
||||
@Nullable JsonSchemaType schemaType,
|
||||
@NotNull JsonValidationHost consumer,
|
||||
@NotNull JsonComplianceCheckerOptions options) {
|
||||
return checkString(propValue.getDelegate(), schema, consumer, options);
|
||||
}
|
||||
|
||||
private static void checkString(PsiElement propValue,
|
||||
private static boolean checkString(PsiElement propValue,
|
||||
JsonSchemaObject schema,
|
||||
JsonValidationHost consumer) {
|
||||
JsonValidationHost consumer,
|
||||
@NotNull JsonComplianceCheckerOptions options) {
|
||||
String v = getValue(propValue, schema);
|
||||
if (v == null) return;
|
||||
if (v == null) return true;
|
||||
final String value = StringUtil.unquoteString(v);
|
||||
if (schema.getMinLength() != null) {
|
||||
if (value.length() < schema.getMinLength()) {
|
||||
consumer.error(JsonBundle.message("schema.validation.string.shorter.than", schema.getMinLength()), propValue, JsonErrorPriority.LOW_PRIORITY);
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (schema.getMaxLength() != null) {
|
||||
if (value.length() > schema.getMaxLength()) {
|
||||
consumer.error(JsonBundle.message("schema.validation.string.longer.than", schema.getMaxLength()), propValue, JsonErrorPriority.LOW_PRIORITY);
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (schema.getPattern() != null) {
|
||||
if (schema.getPatternError() != null) {
|
||||
consumer.error(JsonBundle.message("schema.validation.invalid.string.pattern", StringUtil.convertLineSeparators(schema.getPatternError())),
|
||||
propValue, JsonErrorPriority.LOW_PRIORITY);
|
||||
return false;
|
||||
}
|
||||
if (!schema.checkByPattern(value)) {
|
||||
consumer.error(JsonBundle.message("schema.validation.string.violates.pattern", StringUtil.convertLineSeparators(schema.getPattern())), propValue, JsonErrorPriority.LOW_PRIORITY);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// I think we are not gonna to support format, there are a couple of RFCs there to check upon..
|
||||
/*
|
||||
if (schema.getFormat() != null) {
|
||||
LOG.info("Unsupported property used: 'format'");
|
||||
}*/
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,14 +16,16 @@ import java.util.Collections;
|
||||
public final class TypeValidation implements JsonSchemaValidation {
|
||||
public static final TypeValidation INSTANCE = new TypeValidation();
|
||||
@Override
|
||||
public void validate(@NotNull JsonValueAdapter propValue,
|
||||
@NotNull JsonSchemaObject schema,
|
||||
@Nullable JsonSchemaType schemaType,
|
||||
@NotNull JsonValidationHost consumer,
|
||||
@NotNull JsonComplianceCheckerOptions options) {
|
||||
public boolean validate(@NotNull JsonValueAdapter propValue,
|
||||
@NotNull JsonSchemaObject schema,
|
||||
@Nullable JsonSchemaType schemaType,
|
||||
@NotNull JsonValidationHost consumer,
|
||||
@NotNull JsonComplianceCheckerOptions options) {
|
||||
JsonSchemaType otherType = JsonSchemaAnnotatorChecker.getMatchingSchemaType(schema, schemaType);
|
||||
if (otherType != null && !otherType.equals(schemaType) && !otherType.equals(propValue.getAlternateType(schemaType))) {
|
||||
consumer.typeError(propValue.getDelegate(), propValue.getAlternateType(schemaType), JsonSchemaAnnotatorChecker.getExpectedTypes(Collections.singleton(schema)));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user