WEB-20415 JSON Schema: support references to definitions

This commit is contained in:
irengrig
2016-04-04 12:30:45 +02:00
parent 144d9902ea
commit 10255d09e8
20 changed files with 743 additions and 20 deletions
@@ -0,0 +1,69 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jetbrains.jsonSchema;
import com.intellij.icons.AllIcons;
import com.intellij.json.JsonLanguage;
import com.intellij.openapi.fileTypes.LanguageFileType;
import com.intellij.openapi.fileTypes.ex.FileTypeIdentifiableByVirtualFile;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.project.ProjectUtil;
import com.intellij.openapi.vfs.VirtualFile;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
/**
* @author Irina.Chernushina on 4/1/2016.
*/
public class JsonSchemaFileType extends LanguageFileType implements FileTypeIdentifiableByVirtualFile {
public static JsonSchemaFileType INSTANCE = new JsonSchemaFileType();
public JsonSchemaFileType() {
super(JsonLanguage.INSTANCE);
}
@NotNull
@Override
public String getName() {
return "JSON Schema";
}
@NotNull
@Override
public String getDescription() {
return "JSON Schema file";
}
@NotNull
@Override
public String getDefaultExtension() {
return "json";
}
@Nullable
@Override
public Icon getIcon() {//todo change icon
return AllIcons.FileTypes.Json;
}
@Override
public boolean isMyFileType(@NotNull VirtualFile file) {
final Project project = ProjectUtil.guessProjectForFile(file);
return project != null && JsonSchemaMappingsProjectConfiguration.getInstance(project).isRegisteredSchemaFile(file);
}
}
@@ -0,0 +1,30 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jetbrains.jsonSchema;
import com.intellij.openapi.fileTypes.FileTypeConsumer;
import com.intellij.openapi.fileTypes.FileTypeFactory;
import org.jetbrains.annotations.NotNull;
/**
* @author Irina.Chernushina on 4/1/2016.
*/
public class JsonSchemaFileTypeFactory extends FileTypeFactory {
@Override
public void createFileTypes(@NotNull FileTypeConsumer consumer) {
consumer.consume(JsonSchemaFileType.INSTANCE);
}
}
@@ -15,13 +15,13 @@
*/
package com.jetbrains.jsonSchema.extension;
import com.intellij.json.JsonFileType;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.ResourceUtil;
import com.jetbrains.jsonSchema.JsonSchemaFileType;
import com.jetbrains.jsonSchema.JsonSchemaMappingsProjectConfiguration;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -38,6 +38,7 @@ import java.util.List;
*/
public class JsonSchemaProjectSelfProviderFactory {
private static final Logger LOG = Logger.getInstance("#com.jetbrains.jsonSchema.extension.JsonSchemaProjectSelfProviderFactory");
public static final String SCHEMA_JSON_FILE_NAME = "schema.json";
private final List<JsonSchemaFileProvider<Object>> myProviders;
public static JsonSchemaProjectSelfProviderFactory getInstance(final Project project) {
@@ -62,7 +63,7 @@ public class JsonSchemaProjectSelfProviderFactory {
@Override
public boolean isAvailable(@NotNull VirtualFile file) {
if (myProject == null || !JsonFileType.INSTANCE.equals(file.getFileType())) return false;
if (myProject == null || !JsonSchemaFileType.INSTANCE.equals(file.getFileType())) return false;
return JsonSchemaMappingsProjectConfiguration.getInstance(myProject).isRegisteredSchemaFile(file);
}
@@ -76,7 +77,7 @@ public class JsonSchemaProjectSelfProviderFactory {
@NotNull
@Override
public String getName() {
return "schema.json";
return SCHEMA_JSON_FILE_NAME;
}
@NotNull
@@ -0,0 +1,76 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jetbrains.jsonSchema.extension.schema;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiReference;
import com.intellij.psi.PsiReferenceBase;
import com.intellij.psi.impl.source.resolve.ResolveCache;
import com.intellij.util.ArrayUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* @author Irina.Chernushina on 3/31/2016.
*/
public abstract class JsonSchemaBaseReference<T extends PsiElement> extends PsiReferenceBase<T> {
public JsonSchemaBaseReference(T element, TextRange textRange) {
super(element, textRange, true);
}
@Nullable
@Override
public PsiElement resolve() {
return ResolveCache.getInstance(getElement().getProject()).resolveWithCaching(this, MyResolver.INSTANCE, false, false);
}
@Nullable
public abstract PsiElement resolveInner();
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
JsonSchemaBaseReference that = (JsonSchemaBaseReference)o;
if (!myElement.equals(that.myElement)) return false;
return true;
}
@Override
public int hashCode() {
return myElement.hashCode();
}
@NotNull
@Override
public Object[] getVariants() {
return ArrayUtil.EMPTY_OBJECT_ARRAY;
}
private static class MyResolver implements ResolveCache.Resolver {
private static final MyResolver INSTANCE = new MyResolver();
@Nullable
public PsiElement resolve(@NotNull PsiReference ref, boolean incompleteCode) {
return ((JsonSchemaBaseReference)ref).resolveInner();
}
}
}
@@ -0,0 +1,101 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jetbrains.jsonSchema.extension.schema;
import com.intellij.json.psi.JsonFile;
import com.intellij.json.psi.JsonProperty;
import com.intellij.json.psi.impl.JsonRecursiveElementVisitor;
import com.intellij.psi.PsiFile;
import com.intellij.util.indexing.*;
import com.intellij.util.io.DataExternalizer;
import com.intellij.util.io.EnumeratorStringDescriptor;
import com.intellij.util.io.IntInlineKeyDescriptor;
import com.intellij.util.io.KeyDescriptor;
import com.jetbrains.jsonSchema.JsonSchemaFileType;
import org.jetbrains.annotations.NotNull;
import java.util.HashMap;
import java.util.Map;
/**
* @author Irina.Chernushina on 4/1/2016.
*/
public class JsonSchemaFileIndex extends FileBasedIndexExtension<String, Integer> {
public static final ID<String, Integer> PROPERTIES_INDEX = ID.create("json.schema.properties.index");
public static final int VERSION = 1;
private IntInlineKeyDescriptor myKeyDescriptor = new IntInlineKeyDescriptor();
@NotNull
@Override
public ID<String, Integer> getName() {
return PROPERTIES_INDEX;
}
@NotNull
@Override
public DataIndexer<String, Integer, FileContent> getIndexer() {
return new DataIndexer<String, Integer, FileContent>() {
@NotNull
@Override
public Map<String, Integer> map(@NotNull FileContent inputData) {
final Map<String, Integer> map = new HashMap<>();
final PsiFile file = inputData.getPsiFile();
if (file instanceof JsonFile) {
file.accept(new JsonRecursiveElementVisitor() {
private String myPrefix = "";
@Override
public void visitProperty(@NotNull JsonProperty property) {
String wasPrefix = myPrefix;
myPrefix = myPrefix + "/" + property.getName();
map.put(myPrefix, property.getTextRange().getStartOffset());
super.visitProperty(property);
myPrefix = wasPrefix;
}
});
}
return map;
}
};
}
@NotNull
@Override
public KeyDescriptor<String> getKeyDescriptor() {
return EnumeratorStringDescriptor.INSTANCE;
}
@NotNull
@Override
public DataExternalizer<Integer> getValueExternalizer() {
return myKeyDescriptor;
}
@Override
public int getVersion() {
return VERSION;
}
@NotNull
@Override
public FileBasedIndex.InputFilter getInputFilter() {
return new DefaultFileTypeSpecificInputFilter(JsonSchemaFileType.INSTANCE);
}
@Override
public boolean dependsOnFileContent() {
return true;
}
}
@@ -0,0 +1,93 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jetbrains.jsonSchema.extension.schema;
import com.intellij.json.psi.JsonValue;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.util.ProcessingContext;
import com.intellij.util.indexing.FileBasedIndex;
import com.jetbrains.jsonSchema.ide.JsonSchemaService;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* @author Irina.Chernushina on 3/31/2016.
*/
public class JsonSchemaRefReferenceProvider extends PsiReferenceProvider {
@NotNull
@Override
public PsiReference[] getReferencesByElement(@NotNull PsiElement element, @NotNull ProcessingContext context) {
return new PsiReference[] {new JsonSchemaRefReference((JsonValue)element)};
}
private static class JsonSchemaRefReference extends JsonSchemaBaseReference<JsonValue> {
public JsonSchemaRefReference(JsonValue element) {
super(element, ElementManipulators.getValueTextRange(element));
}
@NotNull
@Override
public String getCanonicalText() {
return StringUtil.unquoteString(super.getCanonicalText());
}
@Nullable
@Override
public PsiElement resolveInner() {
final FileBasedIndex index = FileBasedIndex.getInstance();
final String text = getCanonicalText();
String id = null;
String ref = text.substring(1);
if (!text.startsWith("#")) {
final int idx = text.indexOf("#");
if (idx <= 0) return null;
id = text.substring(0, idx);
ref = text.substring(idx + 1);
}
final Ref<Pair<VirtualFile, Integer>> reference = new Ref<>();
final GlobalSearchScope filter = id == null ? GlobalSearchScope.fileScope(getElement().getContainingFile()) :
GlobalSearchScope.allScope(getElement().getProject());
String finalId = id;
index.processValues(JsonSchemaFileIndex.PROPERTIES_INDEX, ref, null, new FileBasedIndex.ValueProcessor<Integer>() {
@Override
public boolean process(VirtualFile file, Integer value) {
if (finalId != null) {
if (!JsonSchemaService.Impl.getEx(getElement().getProject()).checkFileForId(finalId, file)) {
return true;
}
}
reference.set(Pair.create(file, value));
return false;
}
}, filter);
if (!reference.isNull()) {
final Pair<VirtualFile, Integer> pair = reference.get();
final PsiFile file = getElement().getManager().findFile(pair.getFirst());
if (file != null) {
return file.findElementAt(pair.getSecond());
}
}
return null;
}
}
}
@@ -0,0 +1,73 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jetbrains.jsonSchema.extension.schema;
import com.intellij.json.psi.JsonProperty;
import com.intellij.json.psi.JsonValue;
import com.intellij.openapi.project.Project;
import com.intellij.patterns.PlatformPatterns;
import com.intellij.patterns.PsiElementPattern;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiReferenceContributor;
import com.intellij.psi.PsiReferenceRegistrar;
import com.intellij.psi.filters.ElementFilter;
import com.intellij.psi.filters.position.FilterPattern;
import com.jetbrains.jsonSchema.JsonSchemaFileType;
import com.jetbrains.jsonSchema.JsonSchemaMappingsProjectConfiguration;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* @author Irina.Chernushina on 3/31/2016.
*/
public class JsonSchemaReferenceContributor extends PsiReferenceContributor {
private static final PsiElementPattern.Capture<JsonValue> REF_PATTERN = createPropertyValuePattern("$ref");
@Override
public void registerReferenceProviders(@NotNull PsiReferenceRegistrar registrar) {
registrar.registerReferenceProvider(REF_PATTERN, new JsonSchemaRefReferenceProvider());
}
private static PsiElementPattern.Capture<JsonValue> createPropertyValuePattern(@NotNull final String propertyName) {
return PlatformPatterns.psiElement(JsonValue.class).and(new FilterPattern(new ElementFilter() {
@Override
public boolean isAcceptable(Object element, @Nullable PsiElement context) {
if (element instanceof JsonValue) {
if (!isSchemaFile((PsiElement)element)) return false;
if (((JsonValue)element).getParent() instanceof JsonProperty &&
((JsonProperty)((JsonValue)element).getParent()).getValue() == element) {
return propertyName.equals(((JsonProperty)((JsonValue)element).getParent()).getName());
}
}
return false;
}
@Override
public boolean isClassAcceptable(Class hintClass) {
return true;
}
}));
}
private static boolean isSchemaFile(@NotNull final PsiElement element) {
final Project project = element.getProject();
final PsiFile file = element.getContainingFile();
if (!JsonSchemaFileType.INSTANCE.equals(file.getFileType())) return false;
return file.getVirtualFile() != null &&
JsonSchemaMappingsProjectConfiguration.getInstance(project).isRegisteredSchemaFile(file.getVirtualFile());
}
}
@@ -9,6 +9,7 @@ import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.Consumer;
import com.jetbrains.jsonSchema.impl.JsonSchemaServiceEx;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -18,12 +19,12 @@ import java.util.List;
public interface JsonSchemaService {
class Impl {
@Nullable
public static JsonSchemaService get(@NotNull Project project) {
return ServiceManager.getService(project, JsonSchemaService.class);
}
public static JsonSchemaServiceEx getEx(@NotNull Project project) {
return (JsonSchemaServiceEx) ServiceManager.getService(project, JsonSchemaService.class);
}
}
@Nullable
@@ -15,16 +15,17 @@
*/
package com.jetbrains.jsonSchema.impl;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.Consumer;
import com.intellij.util.PairConsumer;
import com.intellij.util.containers.BidirectionalMap;
import com.intellij.util.containers.MultiMap;
import com.jetbrains.jsonSchema.extension.JsonSchemaProjectSelfProviderFactory;
import com.jetbrains.jsonSchema.extension.SchemaType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.util.*;
import static com.jetbrains.jsonSchema.impl.JsonSchemaReader.LOG;
@@ -39,12 +40,9 @@ public class JsonSchemaExportedDefinitions {
private final BidirectionalMap<String, Pair<SchemaType, ?>> myId2Key;
private final MultiMap<Pair<SchemaType, ?>, Pair<SchemaType, ?>> myCrossDependencies;
private final Map<String, Map<String, JsonSchemaObject>> myMap;
private final Project myProject;
@NotNull private final Consumer<PairConsumer<Pair<SchemaType, ?>, Consumer<Consumer<JsonSchemaObject>>>> mySchemasIterator;
public JsonSchemaExportedDefinitions(@Nullable final Project project,
@NotNull Consumer<PairConsumer<Pair<SchemaType, ?>, Consumer<Consumer<JsonSchemaObject>>>> schemasIterator) {
myProject = project;
public JsonSchemaExportedDefinitions(@NotNull Consumer<PairConsumer<Pair<SchemaType, ?>, Consumer<Consumer<JsonSchemaObject>>>> schemasIterator) {
mySchemasIterator = schemasIterator;
myLock = new Object();
myMap = new HashMap<>();
@@ -131,4 +129,19 @@ public class JsonSchemaExportedDefinitions {
}
return dirtyKeys;
}
public boolean checkFileForId(@NotNull final String id, @NotNull final VirtualFile file) {
final Pair<SchemaType, ?> pair;
synchronized (myLock) {
ensureInitialized();
pair = myId2Key.get(id);
}
if (pair == null) return false;
if (SchemaType.schema.equals(pair.getFirst())) return JsonSchemaProjectSelfProviderFactory.SCHEMA_JSON_FILE_NAME
.equals(file.getName());
if (SchemaType.embeddedSchema.equals(pair.getFirst())) return file.getName().equals(pair.getSecond());
if (SchemaType.userSchema.equals(pair.getFirst())) return pair.getSecond() != null &&
pair.getSecond().equals(new File(file.getPath()));
return false;
}
}
@@ -8,7 +8,7 @@ import java.util.Map;
/**
* @author Irina.Chernushina on 8/28/2015.
*/
class JsonSchemaObject {
public class JsonSchemaObject {
private Map<String, JsonSchemaObject> myDefinitions;
private Map<String, JsonSchemaObject> myProperties;
private Map<String, JsonSchemaObject> myPatternProperties;
@@ -162,7 +162,7 @@ public class JsonSchemaReader {
}
@Nullable
static JsonSchemaObject findDefinition(@NotNull Pair<SchemaType, ?> key,
public static JsonSchemaObject findDefinition(@NotNull Pair<SchemaType, ?> key,
@NotNull String ref,
@NotNull final JsonSchemaObject root,
@NotNull final Map<String, JsonSchemaObject> ids,
@@ -0,0 +1,28 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jetbrains.jsonSchema.impl;
import com.intellij.openapi.vfs.VirtualFile;
import com.jetbrains.jsonSchema.ide.JsonSchemaService;
import org.jetbrains.annotations.NotNull;
/**
* @author Irina.Chernushina on 4/1/2016.
*/
public interface JsonSchemaServiceEx extends JsonSchemaService {
boolean checkFileForId(@NotNull String id, @NotNull VirtualFile file);
}
@@ -29,7 +29,6 @@ import com.jetbrains.jsonSchema.extension.JsonSchemaFileProvider;
import com.jetbrains.jsonSchema.extension.JsonSchemaImportedProviderMarker;
import com.jetbrains.jsonSchema.extension.JsonSchemaProviderFactory;
import com.jetbrains.jsonSchema.extension.SchemaType;
import com.jetbrains.jsonSchema.ide.JsonSchemaService;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -38,7 +37,7 @@ import java.io.IOException;
import java.io.Reader;
import java.util.*;
public class JsonSchemaServiceImpl implements JsonSchemaService {
public class JsonSchemaServiceImpl implements JsonSchemaServiceEx {
private static final Logger LOGGER = Logger.getInstance(JsonSchemaServiceImpl.class);
private static final Logger RARE_LOGGER = RareLogger.wrap(LOGGER, false);
@Nullable
@@ -50,8 +49,8 @@ public class JsonSchemaServiceImpl implements JsonSchemaService {
public JsonSchemaServiceImpl(@Nullable Project project) {
myLock = new Object();
myProject = project;
myDefinitions = new JsonSchemaExportedDefinitions(project,
new Consumer<PairConsumer<Pair<SchemaType, ?>, Consumer<Consumer<JsonSchemaObject>>>>() {
myDefinitions = new JsonSchemaExportedDefinitions(
new Consumer<PairConsumer<Pair<SchemaType, ?>, Consumer<Consumer<JsonSchemaObject>>>>() {
@Override
public void consume(PairConsumer<Pair<SchemaType, ?>, Consumer<Consumer<JsonSchemaObject>>> consumer) {
iterateSchemas(consumer);
@@ -147,6 +146,13 @@ public class JsonSchemaServiceImpl implements JsonSchemaService {
}
catch (Exception e) {
logException(provider, e);
} finally {
if (reader != null) try {
reader.close();
}
catch (IOException e) {
logException(provider, e);
}
}
return null;
}
@@ -303,4 +309,9 @@ public class JsonSchemaServiceImpl implements JsonSchemaService {
return myDocumentationProvider;
}
}
@Override
public boolean checkFileForId(@NotNull final String id, @NotNull final VirtualFile file) {
return myDefinitions.checkFileForId(id, file);
}
}
@@ -21,9 +21,15 @@ import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.PathManager;
import com.intellij.openapi.application.ex.PathManagerEx;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.extensions.AreaPicoContainer;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.fileTypes.FileTypeManager;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiReference;
import com.jetbrains.jsonSchema.ide.JsonSchemaService;
import com.jetbrains.jsonSchema.schemaFile.TestJsonSchemaMappingsProjectConfiguration;
import org.junit.Assert;
import java.util.Collections;
@@ -33,6 +39,15 @@ import java.util.Collections;
*/
public class JsonSchemaCrossReferencesTest extends CompletionTestCase {
private final static String BASE_PATH = "/tests/testData/jsonSchema/crossReferences";
private final static String BASE_SCHEMA_RESOLVE_PATH = "/tests/testData/jsonSchema/schemaFile/resolve";
private FileTypeManager myFileTypeManager;
@Override
public void setUp() throws Exception {
super.setUp();
myFileTypeManager = FileTypeManager.getInstance();
}
@Override
protected String getTestDataPath() {
@@ -173,4 +188,63 @@ public class JsonSchemaCrossReferencesTest extends CompletionTestCase {
complete();
assertStringItems("\"one1\"", "\"two1\"");
}
public void testJsonSchemaRefsCrossResolve() throws Exception {
configureByFiles(null, BASE_SCHEMA_RESOLVE_PATH + "/referencingSchema.json", BASE_SCHEMA_RESOLVE_PATH + "/localRefSchema.json");
String moduleDir = null;
VirtualFile moduleFile = null;
VirtualFile[] children = getProject().getBaseDir().getChildren();
for (VirtualFile child : children) {
if (child.isDirectory()) {
moduleDir = child.getName();
moduleFile = child;
break;
}
}
Assert.assertNotNull(moduleDir);
AreaPicoContainer container = Extensions.getArea(getProject()).getPicoContainer();
final String key = JsonSchemaMappingsProjectConfiguration.class.getName();
container.unregisterComponent(key);
container.registerComponentImplementation(key, TestJsonSchemaMappingsProjectConfiguration.class);
final JsonSchemaMappingsProjectConfiguration instance = JsonSchemaMappingsProjectConfiguration.getInstance(getProject());
final JsonSchemaMappingsConfigurationBase.SchemaInfo base =
new JsonSchemaMappingsConfigurationBase.SchemaInfo("base", "/" + moduleDir + "/localRefSchema.json", false, Collections.emptyList());
instance.addSchema(base);
final JsonSchemaMappingsConfigurationBase.SchemaInfo inherited
= new JsonSchemaMappingsConfigurationBase.SchemaInfo("inherited", "/" + moduleDir + "/referencingSchema.json", false, Collections.emptyList());
instance.addSchema(inherited);
try {
ApplicationManager.getApplication().runWriteAction(new Runnable() {
@Override
public void run() {
myFileTypeManager.associatePattern(JsonSchemaFileType.INSTANCE, "*Schema.json");
}
});
int offset = myEditor.getCaretModel().getPrimaryCaret().getOffset();
final PsiReference referenceAt = myFile.findReferenceAt(offset);
Assert.assertNotNull(referenceAt);
final PsiElement resolve = referenceAt.resolve();
Assert.assertNotNull(resolve);
Assert.assertEquals("\"baseEnum\"", resolve.getText());
ApplicationManager.getApplication().runWriteAction(new Runnable() {
@Override
public void run() {
myFileTypeManager.removeAssociatedExtension(JsonSchemaFileType.INSTANCE, "*Schema.json");
}
});
} finally {
container.unregisterComponent(key);
container.registerComponentImplementation(key, JsonSchemaMappingsProjectConfiguration.class);
}
instance.removeSchema(inherited);
instance.removeSchema(base);
}
}
@@ -1,7 +1,8 @@
package com.jetbrains.jsonSchema;
import com.intellij.json.JsonFileType;
import com.intellij.json.JsonLanguage;
import com.intellij.openapi.fileTypes.LanguageFileType;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.vfs.VirtualFile;
import com.jetbrains.jsonSchema.extension.JsonSchemaFileProvider;
@@ -22,7 +23,7 @@ public class JsonSchemaTestProvider implements JsonSchemaFileProvider<String> {
@Override
public boolean isAvailable(@NotNull VirtualFile file) {
return file.getFileType() == JsonFileType.INSTANCE;
return file.getFileType() instanceof LanguageFileType && ((LanguageFileType)file.getFileType()).getLanguage().isKindOf(JsonLanguage.INSTANCE);
}
@Nullable
@@ -0,0 +1,94 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jetbrains.jsonSchema.schemaFile;
import com.intellij.openapi.application.PathManager;
import com.intellij.openapi.application.ex.PathManagerEx;
import com.intellij.openapi.extensions.AreaPicoContainer;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.fileTypes.FileTypeManager;
import com.intellij.openapi.fileTypes.ex.FileTypeManagerEx;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiReference;
import com.intellij.testFramework.fixtures.LightPlatformCodeInsightFixtureTestCase;
import com.jetbrains.jsonSchema.JsonSchemaFileType;
import com.jetbrains.jsonSchema.JsonSchemaMappingsProjectConfiguration;
import org.junit.Assert;
/**
* @author Irina.Chernushina on 4/1/2016.
*/
public class JsonSchemaFileResolveTest extends LightPlatformCodeInsightFixtureTestCase {
private final static String BASE_PATH = "/tests/testData/jsonSchema/schemaFile/resolve";
private FileTypeManager myFileTypeManager;
@Override
public void setUp() throws Exception {
super.setUp();
myFileTypeManager = FileTypeManagerEx.getInstanceEx();
}
@Override
protected String getTestDataPath() {
PathManagerEx.TestDataLookupStrategy strategy = PathManagerEx.guessTestDataLookupStrategy();
if (strategy.equals(PathManagerEx.TestDataLookupStrategy.COMMUNITY)) {
return PathManager.getHomePath() + "/json" + BASE_PATH;
}
return PathManager.getHomePath() + "/community/json" + BASE_PATH;
}
public void testResolveLocalRef() throws Exception {
AreaPicoContainer container = Extensions.getArea(getProject()).getPicoContainer();
final String key = JsonSchemaMappingsProjectConfiguration.class.getName();
container.unregisterComponent(key);
container.registerComponentImplementation(key, TestJsonSchemaMappingsProjectConfiguration.class);
try {
myFileTypeManager.associatePattern(JsonSchemaFileType.INSTANCE, "*Schema.json");
PsiReference position = myFixture.getReferenceAtCaretPosition("localRefSchema.json");
Assert.assertNotNull(position);
PsiElement resolve = position.resolve();
Assert.assertNotNull(resolve);
Assert.assertEquals("\"baseEnum\"", resolve.getText());
myFileTypeManager.removeAssociatedExtension(JsonSchemaFileType.INSTANCE, "*Schema.json");
} finally {
container.unregisterComponent(key);
container.registerComponentImplementation(key, JsonSchemaMappingsProjectConfiguration.class);
}
}
public void testResolveExternalRef() throws Exception {
AreaPicoContainer container = Extensions.getArea(getProject()).getPicoContainer();
final String key = JsonSchemaMappingsProjectConfiguration.class.getName();
container.unregisterComponent(key);
container.registerComponentImplementation(key, TestJsonSchemaMappingsProjectConfiguration.class);
try {
myFileTypeManager.associatePattern(JsonSchemaFileType.INSTANCE, "*Schema.json");
PsiReference position = myFixture.getReferenceAtCaretPosition("localRefSchema.json");
Assert.assertNotNull(position);
PsiElement resolve = position.resolve();
Assert.assertNotNull(resolve);
Assert.assertEquals("\"baseEnum\"", resolve.getText());
myFileTypeManager.removeAssociatedExtension(JsonSchemaFileType.INSTANCE, "*Schema.json");
} finally {
container.unregisterComponent(key);
container.registerComponentImplementation(key, JsonSchemaMappingsProjectConfiguration.class);
}
}
}
@@ -0,0 +1,35 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jetbrains.jsonSchema.schemaFile;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import com.jetbrains.jsonSchema.JsonSchemaMappingsProjectConfiguration;
import org.jetbrains.annotations.NotNull;
/**
* @author Irina.Chernushina on 4/1/2016.
*/
public class TestJsonSchemaMappingsProjectConfiguration extends JsonSchemaMappingsProjectConfiguration {
public TestJsonSchemaMappingsProjectConfiguration(Project project) {
super(project);
}
@Override
public boolean isRegisteredSchemaFile(@NotNull VirtualFile file) {
return true;
}
}
@@ -0,0 +1,12 @@
{
"id": "https://myurl",
"properties": {
"baseEnum": {
"type": "string",
"enum": ["one", "two"]
},
"smth": {
"$ref" : "#/properties/baseEnum<caret>"
}
}
}
@@ -0,0 +1,7 @@
{
"properties": {
"smth": {
"$ref" : "https://myurl#/properties/baseEnum<caret>"
}
}
}
@@ -16,6 +16,7 @@
<extensions defaultExtensionNs="com.intellij">
<fileTypeFactory implementation="com.intellij.json.JsonFileTypeFactory"/>
<fileTypeFactory implementation="com.jetbrains.jsonSchema.JsonSchemaFileTypeFactory"/>
<lang.parserDefinition language="JSON" implementationClass="com.intellij.json.JsonParserDefinition"/>
<lang.syntaxHighlighterFactory language="JSON" implementationClass="com.intellij.json.highlighting.JsonSyntaxHighlighterFactory"/>
<psi.treeChangePreprocessor implementation="com.intellij.json.psi.impl.JsonTreeChangePreprocessor"/>
@@ -73,6 +74,9 @@
<refactoring.elementListenerProvider implementation="com.jetbrains.jsonSchema.JsonSchemaRefactoringListenerProvider"/>
<editorNotificationProvider implementation="com.jetbrains.jsonSchema.impl.JsonSchemaConflictNotificationProvider"/>
<projectService serviceImplementation="com.jetbrains.jsonSchema.extension.JsonSchemaProjectSelfProviderFactory"/>
<fileBasedIndex implementation="com.jetbrains.jsonSchema.extension.schema.JsonSchemaFileIndex"/>
<psi.referenceContributor language="JSON" implementation="com.jetbrains.jsonSchema.extension.schema.JsonSchemaReferenceContributor"/>
</extensions>
<extensions defaultExtensionNs="JavaScript">
<JsonSchema.ProviderFactory implementation="com.jetbrains.jsonSchema.extension.JsonSchemaImportedProviderFactory"/>