mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
prepare for WEB-32826 JSON schema: Provide quick fixes
This commit is contained in:
@@ -10,6 +10,7 @@ import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.ObjectUtils;
|
||||
import com.intellij.util.SmartList;
|
||||
import com.intellij.util.ThreeState;
|
||||
@@ -33,14 +34,14 @@ import java.util.stream.Collectors;
|
||||
class JsonSchemaAnnotatorChecker {
|
||||
private static final Set<JsonSchemaType> PRIMITIVE_TYPES =
|
||||
ContainerUtil.set(JsonSchemaType._integer, JsonSchemaType._number, JsonSchemaType._boolean, JsonSchemaType._string, JsonSchemaType._null);
|
||||
private final Map<PsiElement, String> myErrors;
|
||||
private final Map<PsiElement, JsonValidationError> myErrors;
|
||||
private boolean myHadTypeError;
|
||||
|
||||
protected JsonSchemaAnnotatorChecker() {
|
||||
myErrors = new HashMap<>();
|
||||
}
|
||||
|
||||
public Map<PsiElement, String> getErrors() {
|
||||
public Map<PsiElement, JsonValidationError> getErrors() {
|
||||
return myErrors;
|
||||
}
|
||||
|
||||
@@ -91,8 +92,8 @@ class JsonSchemaAnnotatorChecker {
|
||||
final JsonSchemaAnnotatorChecker checker = new JsonSchemaAnnotatorChecker();
|
||||
|
||||
for (JsonSchemaAnnotatorChecker ch: list) {
|
||||
for (Map.Entry<PsiElement, String> element: ch.myErrors.entrySet()) {
|
||||
if (skipErrors.contains(element.getValue())) {
|
||||
for (Map.Entry<PsiElement, JsonValidationError> element: ch.myErrors.entrySet()) {
|
||||
if (skipErrors.contains(element.getValue().getMessage())) {
|
||||
continue;
|
||||
}
|
||||
checker.myErrors.put(element.getKey(), element.getValue());
|
||||
@@ -102,21 +103,33 @@ class JsonSchemaAnnotatorChecker {
|
||||
}
|
||||
|
||||
private void error(final String error, final PsiElement holder) {
|
||||
error(error, holder, JsonValidationError.FixableIssueKind.None, null);
|
||||
}
|
||||
|
||||
private void error(final PsiElement newHolder, JsonValidationError error) {
|
||||
error(error.getMessage(), newHolder, error.getFixableIssueKind(), error.getIssueData());
|
||||
}
|
||||
|
||||
private void error(final String error, final PsiElement holder, JsonValidationError.FixableIssueKind fixableIssueKind, JsonValidationError.IssueData data) {
|
||||
if (myErrors.containsKey(holder)) return;
|
||||
myErrors.put(holder, error);
|
||||
myErrors.put(holder, new JsonValidationError(error, fixableIssueKind, data));
|
||||
}
|
||||
|
||||
private void typeError(final @NotNull PsiElement value, final @NotNull JsonSchemaType... allowedTypes) {
|
||||
if (allowedTypes.length > 0) {
|
||||
if (allowedTypes.length == 1) {
|
||||
error(String.format("Type is not allowed. Expected: %s.", allowedTypes[0].getName()), value);
|
||||
error(String.format("Type is not allowed. Expected: %s.", allowedTypes[0].getName()), value,
|
||||
JsonValidationError.FixableIssueKind.ProhibitedType,
|
||||
new JsonValidationError.TypeMismatchIssueData(allowedTypes));
|
||||
} else {
|
||||
final String typesText = Arrays.stream(allowedTypes)
|
||||
.map(JsonSchemaType::getName)
|
||||
.distinct()
|
||||
.sorted(Comparator.naturalOrder())
|
||||
.collect(Collectors.joining(", "));
|
||||
error(String.format("Type is not allowed. Expected one of: %s.", typesText), value);
|
||||
error(String.format("Type is not allowed. Expected one of: %s.", typesText), value,
|
||||
JsonValidationError.FixableIssueKind.ProhibitedType,
|
||||
new JsonValidationError.TypeMismatchIssueData(allowedTypes));
|
||||
}
|
||||
} else {
|
||||
error("Type is not allowed", value);
|
||||
@@ -258,7 +271,9 @@ class JsonSchemaAnnotatorChecker {
|
||||
final JsonSchemaVariantsTreeBuilder.Step step = JsonSchemaVariantsTreeBuilder.Step.createPropertyStep(name);
|
||||
final Pair<ThreeState, JsonSchemaObject> pair = step.step(schema, true);
|
||||
if (ThreeState.NO.equals(pair.getFirst()) && !set.contains(name)) {
|
||||
error(JsonBundle.message("json.schema.annotation.not.allowed.property", name), property.getDelegate());
|
||||
error(JsonBundle.message("json.schema.annotation.not.allowed.property", name), property.getDelegate(),
|
||||
JsonValidationError.FixableIssueKind.ProhibitedProperty,
|
||||
new JsonValidationError.ProhibitedPropertyIssueData(name));
|
||||
}
|
||||
else if (ThreeState.UNSURE.equals(pair.getFirst()) && property.getValue() != null) {
|
||||
checkObjectBySchemaRecordErrors(pair.getSecond(), property.getValue());
|
||||
@@ -271,7 +286,9 @@ class JsonSchemaAnnotatorChecker {
|
||||
if (required != null) {
|
||||
for (String req : required) {
|
||||
if (!set.contains(req)) {
|
||||
error("Missing required property '" + req + "'", value.getDelegate());
|
||||
error("Missing required property '" + req + "'", value.getDelegate(),
|
||||
JsonValidationError.FixableIssueKind.MissingProperty,
|
||||
new JsonValidationError.MissingPropertyIssueData(req, resolvePropertyType(schema, req)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -289,7 +306,9 @@ class JsonSchemaAnnotatorChecker {
|
||||
for (String s : list) {
|
||||
if (!set.contains(s)) {
|
||||
error("Dependency is violated: '" + s + "' must be specified, since '" + entry.getKey() + "' is specified",
|
||||
value.getDelegate());
|
||||
value.getDelegate(),
|
||||
JsonValidationError.FixableIssueKind.MissingProperty,
|
||||
new JsonValidationError.MissingPropertyIssueData(s, resolvePropertyType(schema, s)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -308,6 +327,27 @@ class JsonSchemaAnnotatorChecker {
|
||||
validateAsJsonSchema(object.getDelegate());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static JsonSchemaType resolvePropertyType(@NotNull JsonSchemaObject schema, String req) {
|
||||
JsonSchemaType type = null;
|
||||
if (schema.getProperties().containsKey(req)) {
|
||||
type = schema.getProperties().get(req).getType();
|
||||
}
|
||||
else {
|
||||
JsonSchemaObject propertySchema = schema.getMatchingPatternPropertySchema(req);
|
||||
if (propertySchema != null) {
|
||||
type = propertySchema.getType();
|
||||
}
|
||||
else {
|
||||
JsonSchemaObject additionalPropertiesSchema = schema.getAdditionalPropertiesSchema();
|
||||
if (additionalPropertiesSchema != null) {
|
||||
type = additionalPropertiesSchema.getType();
|
||||
}
|
||||
}
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
private void validateAsJsonSchema(@NotNull PsiElement objElement) {
|
||||
final JsonObject object = ObjectUtils.tryCast(objElement, JsonObject.class);
|
||||
if (object == null) return;
|
||||
@@ -402,7 +442,9 @@ class JsonSchemaAnnotatorChecker {
|
||||
if (equalsIgnoreQuotesAndCase(object.toString(), text)) return;
|
||||
}
|
||||
}
|
||||
error("Value should be one of: [" + StringUtil.join(objects, o -> o.toString(), ", ") + "]", value);
|
||||
error("Value should be one of: [" + StringUtil.join(objects, o -> o.toString(), ", ") + "]", value,
|
||||
JsonValidationError.FixableIssueKind.NonEnumValue,
|
||||
new JsonValidationError.NonEnumValueIssueData(ContainerUtil.map(objects, o -> o.toString()).toArray(ArrayUtil.EMPTY_STRING_ARRAY)));
|
||||
}
|
||||
|
||||
private static boolean equalsIgnoreQuotesAndCase(@NotNull final String s1, @NotNull final String s2) {
|
||||
@@ -629,7 +671,9 @@ class JsonSchemaAnnotatorChecker {
|
||||
value = Integer.valueOf(propValue.getText());
|
||||
}
|
||||
catch (NumberFormatException e) {
|
||||
error("Integer value expected", propValue);
|
||||
error("Integer value expected", propValue,
|
||||
JsonValidationError.FixableIssueKind.TypeMismatch,
|
||||
new JsonValidationError.TypeMismatchIssueData(new JsonSchemaType[]{schemaType}));
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -638,7 +682,9 @@ class JsonSchemaAnnotatorChecker {
|
||||
value = Double.valueOf(propValue.getText());
|
||||
}
|
||||
catch (NumberFormatException e) {
|
||||
error("Double value expected", propValue);
|
||||
error("Double value expected", propValue,
|
||||
JsonValidationError.FixableIssueKind.TypeMismatch,
|
||||
new JsonValidationError.TypeMismatchIssueData(new JsonSchemaType[]{schemaType}));
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -758,7 +804,7 @@ class JsonSchemaAnnotatorChecker {
|
||||
|
||||
// returns the schema, selected for annotation
|
||||
private JsonSchemaObject processOneOf(@NotNull JsonValueAdapter value, List<JsonSchemaObject> oneOf) {
|
||||
final Map<PsiElement, String> errors = new HashMap<>();
|
||||
final Map<PsiElement, JsonValidationError> errors = new HashMap<>();
|
||||
boolean wasTypeError = false;
|
||||
final List<JsonSchemaObject> correct = new SmartList<>();
|
||||
JsonSchemaObject current = null;
|
||||
@@ -795,8 +841,8 @@ class JsonSchemaAnnotatorChecker {
|
||||
}
|
||||
else {
|
||||
if (!errors.isEmpty()) {
|
||||
for (Map.Entry<PsiElement, String> entry : errors.entrySet()) {
|
||||
error(entry.getValue(), entry.getKey());
|
||||
for (Map.Entry<PsiElement, JsonValidationError> entry : errors.entrySet()) {
|
||||
error(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -809,7 +855,7 @@ class JsonSchemaAnnotatorChecker {
|
||||
|
||||
// returns the schema, selected for annotation
|
||||
private JsonSchemaObject processAnyOf(@NotNull JsonValueAdapter value, List<JsonSchemaObject> anyOf) {
|
||||
final Map<PsiElement, String> errors = new HashMap<>();
|
||||
final Map<PsiElement, JsonValidationError> errors = new HashMap<>();
|
||||
JsonSchemaObject current = null;
|
||||
for (JsonSchemaObject object : anyOf) {
|
||||
final JsonSchemaAnnotatorChecker checker = new JsonSchemaAnnotatorChecker();
|
||||
@@ -825,8 +871,8 @@ class JsonSchemaAnnotatorChecker {
|
||||
}
|
||||
}
|
||||
if (!errors.isEmpty()) {
|
||||
for (Map.Entry<PsiElement, String> entry : errors.entrySet()) {
|
||||
error(entry.getValue(), entry.getKey());
|
||||
for (Map.Entry<PsiElement, JsonValidationError> entry : errors.entrySet()) {
|
||||
error(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
return current;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
package com.jetbrains.jsonSchema.impl;
|
||||
|
||||
import com.intellij.codeInspection.LocalInspectionToolSession;
|
||||
import com.intellij.codeInspection.LocalQuickFix;
|
||||
import com.intellij.codeInspection.ProblemsHolder;
|
||||
import com.intellij.openapi.util.Key;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
@@ -76,11 +77,17 @@ public class JsonSchemaComplianceChecker {
|
||||
|
||||
private void createWarnings(@Nullable JsonSchemaAnnotatorChecker checker) {
|
||||
if (checker != null && ! checker.isCorrect()) {
|
||||
for (Map.Entry<PsiElement, String> entry : checker.getErrors().entrySet()) {
|
||||
for (Map.Entry<PsiElement, JsonValidationError> entry : checker.getErrors().entrySet()) {
|
||||
if (checkIfAlreadyProcessed(entry.getKey())) continue;
|
||||
String value = entry.getValue();
|
||||
String value = entry.getValue().getMessage();
|
||||
if (myMessagePrefix != null) value = myMessagePrefix + value;
|
||||
myHolder.registerProblem(entry.getKey(), value);
|
||||
LocalQuickFix fix = entry.getValue().createFix(entry.getKey());
|
||||
if (fix == null) {
|
||||
myHolder.registerProblem(entry.getKey(), value);
|
||||
}
|
||||
else {
|
||||
myHolder.registerProblem(entry.getKey(), value, fix);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.jetbrains.jsonSchema.impl;
|
||||
|
||||
import com.intellij.codeInspection.LocalQuickFix;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class JsonValidationError {
|
||||
|
||||
public IssueData getIssueData() {
|
||||
return myIssueData;
|
||||
}
|
||||
|
||||
public enum FixableIssueKind {
|
||||
MissingProperty,
|
||||
ProhibitedProperty,
|
||||
NonEnumValue,
|
||||
ProhibitedType,
|
||||
TypeMismatch,
|
||||
None
|
||||
}
|
||||
|
||||
public interface IssueData {
|
||||
|
||||
}
|
||||
|
||||
public static class MissingPropertyIssueData implements IssueData {
|
||||
public final String propertyName;
|
||||
public final JsonSchemaType propertyType;
|
||||
|
||||
public MissingPropertyIssueData(String propertyName, JsonSchemaType propertyType) {
|
||||
this.propertyName = propertyName;
|
||||
this.propertyType = propertyType;
|
||||
}
|
||||
}
|
||||
|
||||
public static class ProhibitedPropertyIssueData implements IssueData {
|
||||
public final String propertyName;
|
||||
|
||||
public ProhibitedPropertyIssueData(String propertyName) {
|
||||
this.propertyName = propertyName;
|
||||
}
|
||||
}
|
||||
|
||||
public static class NonEnumValueIssueData implements IssueData {
|
||||
public final String[] expectedValues;
|
||||
|
||||
public NonEnumValueIssueData(String[] expectedValues) {
|
||||
this.expectedValues = expectedValues;
|
||||
}
|
||||
}
|
||||
|
||||
public static class TypeMismatchIssueData implements IssueData {
|
||||
public final JsonSchemaType[] expectedTypes;
|
||||
|
||||
public TypeMismatchIssueData(JsonSchemaType[] expectedTypes) {
|
||||
this.expectedTypes = expectedTypes;
|
||||
}
|
||||
}
|
||||
|
||||
private final String myMessage;
|
||||
private final FixableIssueKind myFixableIssueKind;
|
||||
private final IssueData myIssueData;
|
||||
|
||||
public JsonValidationError(String message, FixableIssueKind fixableIssueKind, IssueData issueData) {
|
||||
myMessage = message;
|
||||
myFixableIssueKind = fixableIssueKind;
|
||||
myIssueData = issueData;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return myMessage;
|
||||
}
|
||||
|
||||
public FixableIssueKind getFixableIssueKind() {
|
||||
return myFixableIssueKind;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public LocalQuickFix createFix(PsiElement key) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user