diff --git a/com.reprezen.swagedit.core/.classpath b/com.reprezen.swagedit.core/.classpath index b42e18f2..c6935ecd 100644 --- a/com.reprezen.swagedit.core/.classpath +++ b/com.reprezen.swagedit.core/.classpath @@ -1,5 +1,10 @@ + + + + + diff --git a/com.reprezen.swagedit.core/META-INF/MANIFEST.MF b/com.reprezen.swagedit.core/META-INF/MANIFEST.MF index 71ef9f94..41a6de76 100644 --- a/com.reprezen.swagedit.core/META-INF/MANIFEST.MF +++ b/com.reprezen.swagedit.core/META-INF/MANIFEST.MF @@ -14,15 +14,17 @@ Require-Bundle: org.eclipse.ui, org.eclipse.ui.ide, org.eclipse.core.filesystem, org.eclipse.ui.views, - com.fasterxml.jackson.core.jackson-core;bundle-version="2.5.0", - com.fasterxml.jackson.core.jackson-databind;bundle-version="2.5.0", - com.fasterxml.jackson.dataformat.jackson-dataformat-yaml;bundle-version="2.5.0", org.yaml.snakeyaml;bundle-version="1.14.0", org.slf4j.api;bundle-version="1.7.2", org.eclipse.ui.workbench.texteditor Bundle-RequiredExecutionEnvironment: JavaSE-1.7 Bundle-ActivationPolicy: lazy -Export-Package: com.github.fge.jsonschema.core.exceptions, +Export-Package: com.fasterxml.jackson.core, + com.fasterxml.jackson.databind, + com.fasterxml.jackson.databind.node, + com.fasterxml.jackson.dataformat.yaml, + com.github.fge.jackson.jsonpointer, + com.github.fge.jsonschema.core.exceptions, com.github.fge.jsonschema.core.load.configuration, com.github.fge.jsonschema.core.report, com.github.fge.jsonschema.main, @@ -34,8 +36,8 @@ Export-Package: com.github.fge.jsonschema.core.exceptions, com.reprezen.swagedit.core.editor.outline, com.reprezen.swagedit.core.handlers, com.reprezen.swagedit.core.hyperlinks, - com.reprezen.swagedit.core.json.references, - com.reprezen.swagedit.core.model, + com.reprezen.swagedit.core.json, + com.reprezen.swagedit.core.json.references, com.reprezen.swagedit.core.preferences, com.reprezen.swagedit.core.quickfix, com.reprezen.swagedit.core.schema, @@ -54,5 +56,10 @@ Bundle-ClassPath: ., lib/com.googlecode.libphonenumber_6.2.0.jar, lib/com.github.fge.uri-template_0.9.0.jar, lib/org.mozilla.rhino_1.0.0.7R4.jar, - lib/org.apache.commons.lang3_3.2.1.jar + lib/org.apache.commons.lang3_3.2.1.jar, + lib/com.fasterxml.jackson.core.jackson-annotations_2.9.0.jar, + lib/com.fasterxml.jackson.core.jackson-core_2.9.2.jar, + lib/com.fasterxml.jackson.core.jackson-databind_2.9.2.jar, + lib/com.fasterxml.jackson.dataformat.jackson-dataformat-yaml_2.9.2.jar, + lib/org.yaml.snakeyaml_1.18.0.jar Bundle-Activator: com.reprezen.swagedit.core.Activator diff --git a/com.reprezen.swagedit.core/build.properties b/com.reprezen.swagedit.core/build.properties index 0061b942..fd0d1c5b 100644 --- a/com.reprezen.swagedit.core/build.properties +++ b/com.reprezen.swagedit.core/build.properties @@ -2,19 +2,13 @@ source.. = src/ output.. = target/classes/ bin.includes = .,\ META-INF/,\ - lib/,\ - lib/com.github.fge.json-schema-core_1.2.5.jar,\ - lib/com.github.fge.json-schema-validator_2.2.6.jar,\ - lib/com.github.fge.btf_1.2.0.jar,\ - lib/com.github.fge.jackson-coreutils_1.8.0.jar,\ - lib/com.github.fge.msg-simple_1.1.0.jar,\ - lib/joda-time_2.3.0.jar,\ - lib/javax.mail.api_1.4.3.jar,\ - lib/com.googlecode.libphonenumber_6.2.0.jar,\ - lib/com.github.fge.uri-template_0.9.0.jar,\ - lib/org.mozilla.rhino_1.0.0.7R4.jar,\ - lib/org.apache.commons.lang3_3.2.1.jar,\ icons/,\ plugin.xml,\ - resources/ + resources/,\ + lib/,\ + lib/com.fasterxml.jackson.core.jackson-annotations_2.9.0.jar,\ + lib/com.fasterxml.jackson.core.jackson-core_2.9.2.jar,\ + lib/com.fasterxml.jackson.core.jackson-databind_2.9.2.jar,\ + lib/com.fasterxml.jackson.dataformat.jackson-dataformat-yaml_2.9.2.jar,\ + lib/org.yaml.snakeyaml_1.18.0.jar jars.compile.order = . diff --git a/com.reprezen.swagedit.core/lib/com.fasterxml.jackson.core.jackson-annotations_2.9.0.jar b/com.reprezen.swagedit.core/lib/com.fasterxml.jackson.core.jackson-annotations_2.9.0.jar new file mode 100644 index 00000000..c602d75d Binary files /dev/null and b/com.reprezen.swagedit.core/lib/com.fasterxml.jackson.core.jackson-annotations_2.9.0.jar differ diff --git a/com.reprezen.swagedit.core/lib/com.fasterxml.jackson.core.jackson-core_2.9.2.jar b/com.reprezen.swagedit.core/lib/com.fasterxml.jackson.core.jackson-core_2.9.2.jar new file mode 100644 index 00000000..ff26a3f4 Binary files /dev/null and b/com.reprezen.swagedit.core/lib/com.fasterxml.jackson.core.jackson-core_2.9.2.jar differ diff --git a/com.reprezen.swagedit.core/lib/com.fasterxml.jackson.core.jackson-databind_2.9.2.jar b/com.reprezen.swagedit.core/lib/com.fasterxml.jackson.core.jackson-databind_2.9.2.jar new file mode 100644 index 00000000..8a340d89 Binary files /dev/null and b/com.reprezen.swagedit.core/lib/com.fasterxml.jackson.core.jackson-databind_2.9.2.jar differ diff --git a/com.reprezen.swagedit.core/lib/com.fasterxml.jackson.dataformat.jackson-dataformat-yaml_2.9.2.jar b/com.reprezen.swagedit.core/lib/com.fasterxml.jackson.dataformat.jackson-dataformat-yaml_2.9.2.jar new file mode 100644 index 00000000..369be891 Binary files /dev/null and b/com.reprezen.swagedit.core/lib/com.fasterxml.jackson.dataformat.jackson-dataformat-yaml_2.9.2.jar differ diff --git a/com.reprezen.swagedit.core/lib/org.yaml.snakeyaml_1.18.0.jar b/com.reprezen.swagedit.core/lib/org.yaml.snakeyaml_1.18.0.jar new file mode 100644 index 00000000..e3fac8e7 Binary files /dev/null and b/com.reprezen.swagedit.core/lib/org.yaml.snakeyaml_1.18.0.jar differ diff --git a/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/assist/JsonContentAssistProcessor.java b/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/assist/JsonContentAssistProcessor.java index aefb0867..374b30ea 100644 --- a/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/assist/JsonContentAssistProcessor.java +++ b/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/assist/JsonContentAssistProcessor.java @@ -12,6 +12,7 @@ import static org.eclipse.ui.IWorkbenchCommandConstants.EDIT_CONTENT_ASSIST; +import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.List; @@ -52,8 +53,9 @@ import com.reprezen.swagedit.core.Activator.Icons; import com.reprezen.swagedit.core.assist.contexts.ContextType; import com.reprezen.swagedit.core.editor.JsonDocument; +import com.reprezen.swagedit.core.json.JsonModel; +import com.reprezen.swagedit.core.json.JsonRegion; import com.reprezen.swagedit.core.json.references.Messages; -import com.reprezen.swagedit.core.model.Model; import com.reprezen.swagedit.core.templates.SwaggerTemplateContext; import com.reprezen.swagedit.core.utils.SwaggerFileFinder.Scope; @@ -63,9 +65,9 @@ public abstract class JsonContentAssistProcessor extends TemplateCompletionProcessor implements IContentAssistProcessor, ICompletionListener { - private final JsonProposalProvider proposalProvider; - private final JsonReferenceProposalProvider referenceProposalProvider; - private final ContentAssistant contentAssistant; + private final JsonProposalProvider proposalProvider; + private final JsonReferenceProposalProvider referenceProposalProvider; + private final ContentAssistant contentAssistant; /** * The pointer that helps us locate the current position of the cursor inside the document. @@ -85,25 +87,25 @@ public abstract class JsonContentAssistProcessor extends TemplateCompletionProce private boolean isRefCompletion = false; private String[] textMessages; - - public JsonContentAssistProcessor(ContentAssistant ca, String fileContentType) { - this(ca, new JsonProposalProvider(), - new JsonReferenceProposalProvider(ContextType.emptyContentTypeCollection(), fileContentType)); - } - - public JsonContentAssistProcessor(ContentAssistant ca, JsonProposalProvider proposalProvider, JsonReferenceProposalProvider referenceProposalProvider) { + + public JsonContentAssistProcessor(ContentAssistant ca, String fileContentType) { + this(ca, new JsonProposalProvider(), + new JsonReferenceProposalProvider(ContextType.emptyContentTypeCollection(), fileContentType)); + } + + public JsonContentAssistProcessor(ContentAssistant ca, JsonProposalProvider proposalProvider, + JsonReferenceProposalProvider referenceProposalProvider) { this.contentAssistant = ca; this.proposalProvider = proposalProvider; this.referenceProposalProvider = referenceProposalProvider; this.textMessages = initTextMessages(null); } - + protected abstract TemplateStore getTemplateStore(); protected abstract ContextTypeRegistry getContextTypeRegistry(); - - protected abstract String getContextTypeId(Model model, String path); + protected abstract String getContextTypeId(JsonDocument doc, String path); @Override public ICompletionProposal[] computeCompletionProposals(ITextViewer viewer, int documentOffset) { @@ -131,19 +133,31 @@ public ICompletionProposal[] computeCompletionProposals(ITextViewer viewer, int column -= prefix.length(); } - Model model = document.getModel(documentOffset - prefix.length()); - currentPath = model.getPath(line, column); + JsonModel model = null; + try { + model = new JsonModel(document.getSchema(), document.get(), false); + } catch (Exception e) { + try { + model = new JsonModel(document.getSchema(), document.get(0, documentOffset - prefix.length()), true); + } catch (BadLocationException | IOException ee) { + ee.printStackTrace(); + } + } + + JsonRegion range = model.findRegion(line + 1, column + 1); + currentPath = JsonPointer.compile(range.pointer.toString()); + isRefCompletion = referenceProposalProvider.canProvideProposal(model, currentPath); Collection p; if (isRefCompletion) { updateStatus(model); - p = referenceProposalProvider.getProposals(currentPath, document, currentScope); + p = referenceProposalProvider.getProposals(currentPath, model, currentScope); } else { clearStatus(); p = proposalProvider.getProposals(currentPath, model, prefix); } - + final Collection proposals = getCompletionProposals(p, prefix, documentOffset); // compute template proposals if (!isRefCompletion) { @@ -164,10 +178,10 @@ private void maybeSwitchScope(int documentOffset) { currentOffset = documentOffset; } - protected void updateStatus(Model model) { + protected void updateStatus(JsonModel doc) { if (contentAssistant != null) { if (textMessages == null) { - textMessages = initTextMessages(model); + textMessages = initTextMessages(doc); } contentAssistant.setStatusLineVisible(true); contentAssistant.setStatusMessage(textMessages[currentScope.getValue()]); @@ -180,17 +194,17 @@ protected void clearStatus() { } } - protected String[] initTextMessages(Model model) { - IBindingService bindingService = (IBindingService) PlatformUI.getWorkbench().getAdapter(IBindingService.class); - String bindingKey = bindingService.getBestActiveBindingFormattedFor(EDIT_CONTENT_ASSIST); - ContextType contextType = referenceProposalProvider.getContextTypes().get(model, getCurrentPath()); - String context = contextType != null ? contextType.label() : ""; + protected String[] initTextMessages(JsonModel doc) { + IBindingService bindingService = (IBindingService) PlatformUI.getWorkbench().getAdapter(IBindingService.class); + String bindingKey = bindingService.getBestActiveBindingFormattedFor(EDIT_CONTENT_ASSIST); + ContextType contextType = referenceProposalProvider.getContextTypes().get(doc, getCurrentPath()); + String context = contextType != null ? contextType.label() : ""; - return new String[] { // - String.format(Messages.content_assist_proposal_project, bindingKey, context), - String.format(Messages.content_assist_proposal_workspace, bindingKey, context), - String.format(Messages.content_assist_proposal_local, bindingKey, context) }; - } + return new String[] { // + String.format(Messages.content_assist_proposal_project, bindingKey, context), + String.format(Messages.content_assist_proposal_workspace, bindingKey, context), + String.format(Messages.content_assist_proposal_local, bindingKey, context) }; + } protected Collection getCompletionProposals(Collection proposals, String prefix, int offset) { @@ -254,15 +268,15 @@ public IContextInformationValidator getContextInformationValidator() { return null; } - @Override - protected ICompletionProposal createProposal(Template template, TemplateContext context, IRegion region, - int relevance) { + @Override + protected ICompletionProposal createProposal(Template template, TemplateContext context, IRegion region, + int relevance) { if (context instanceof DocumentTemplateContext) { context = new SwaggerTemplateContext((DocumentTemplateContext) context); } - return new StyledTemplateProposal(template, context, region, getImage(template), getTemplateLabel(template), - relevance); - } + return new StyledTemplateProposal(template, context, region, getImage(template), getTemplateLabel(template), + relevance); + } @Override protected Template[] getTemplates(String contextTypeId) { @@ -271,22 +285,23 @@ protected Template[] getTemplates(String contextTypeId) { @Override protected TemplateContextType getContextType(ITextViewer viewer, IRegion region) { - Model model = null; - if (viewer.getDocument() instanceof JsonDocument) { - model = ((JsonDocument)viewer.getDocument()).getModel(); - } - String contextType = getContextTypeId(model, currentPath.toString()); - ContextTypeRegistry registry = getContextTypeRegistry(); - if (registry != null) { - return registry.getContextType(contextType); - } else { - return null; - } + JsonModel model = null; + // if (viewer.getDocument() instanceof JsonDocument) { + // model = ((JsonDocument)viewer.getDocument()).getModel(); + // } + // String contextType = getContextTypeId(model, currentPath.toString()); + // ContextTypeRegistry registry = getContextTypeRegistry(); + // if (registry != null) { + // return registry.getContextType(contextType); + // } else { + // return null; + // } + return null; } @Override protected Image getImage(Template template) { - return Activator.getDefault().getImage(Icons.template_item); + return Activator.getDefault().getImage(Icons.template_item); } protected StyledString getTemplateLabel(Template template) { @@ -323,9 +338,9 @@ private void resetScope() { textMessages = null; currentOffset = -1; } - + protected JsonPointer getCurrentPath() { - return currentPath; + return currentPath; } @Override diff --git a/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/assist/JsonProposalProvider.java b/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/assist/JsonProposalProvider.java index eaafb01b..0f09ad29 100644 --- a/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/assist/JsonProposalProvider.java +++ b/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/assist/JsonProposalProvider.java @@ -24,8 +24,7 @@ import com.google.common.base.Strings; import com.google.common.collect.Lists; import com.reprezen.swagedit.core.assist.ext.ContentAssistExt; -import com.reprezen.swagedit.core.model.AbstractNode; -import com.reprezen.swagedit.core.model.Model; +import com.reprezen.swagedit.core.json.JsonModel; import com.reprezen.swagedit.core.schema.ArrayTypeDefinition; import com.reprezen.swagedit.core.schema.ComplexTypeDefinition; import com.reprezen.swagedit.core.schema.JsonType; @@ -58,36 +57,15 @@ public JsonProposalProvider(ContentAssistExt... extensions) { * @param prefix * @return proposals */ - public Collection getProposals(JsonPointer pointer, Model model, String prefix) { - final AbstractNode node = model.find(pointer); + public Collection getProposals(JsonPointer pointer, JsonModel document, String prefix) { + JsonNode node = document.getContent().at(pointer); if (node == null) { return Collections.emptyList(); } - return getProposals(node.getType(), node, prefix); + return getProposals(document.getType(pointer), node, prefix); } - /** - * Returns all proposals for the node inside the given model located at the given pointer. - * - * @param pointer - * @param model - * @return proposals - */ - public Collection getProposals(JsonPointer pointer, Model model) { - return getProposals(pointer, model, null); - } - - /** - * Returns all proposals for the current node. - * - * @param node - * @return proposals - */ - public Collection getProposals(AbstractNode node) { - return getProposals(node.getType(), node, null); - } - - protected Collection getProposals(TypeDefinition type, AbstractNode node, String prefix) { + protected Collection getProposals(TypeDefinition type, JsonNode node, String prefix) { if (type instanceof ReferenceTypeDefinition) { type = ((ReferenceTypeDefinition) type).resolve(); } @@ -167,7 +145,7 @@ protected Proposal createPropertyProposal(String key, TypeDefinition type) { return new Proposal(key + ":", key, type.getDescription(), labelType); } - protected Collection createObjectProposals(ObjectTypeDefinition type, AbstractNode element, + protected Collection createObjectProposals(ObjectTypeDefinition type, JsonNode element, String prefix) { final Collection proposals = new LinkedHashSet<>(); @@ -209,7 +187,7 @@ protected Collection createObjectProposals(ObjectTypeDefinition type, return proposals; } - protected Collection createArrayProposals(ArrayTypeDefinition type, AbstractNode node) { + protected Collection createArrayProposals(ArrayTypeDefinition type, JsonNode node) { Collection proposals = new LinkedHashSet<>(); if (type.itemsType.getType() == JsonType.ENUM) { @@ -225,7 +203,7 @@ protected Collection createArrayProposals(ArrayTypeDefinition type, Ab return proposals; } - protected Collection createComplextTypeProposals(ComplexTypeDefinition type, AbstractNode node, + protected Collection createComplextTypeProposals(ComplexTypeDefinition type, JsonNode node, String prefix) { final Collection proposals = new LinkedHashSet<>(); @@ -244,7 +222,7 @@ protected Collection enumLiterals(TypeDefinition type) { return literals; } - protected Collection createEnumProposals(TypeDefinition type, AbstractNode node) { + protected Collection createEnumProposals(TypeDefinition type, JsonNode node) { final Collection proposals = new LinkedHashSet<>(); final String subType = type.asJson().has("type") ? // type.asJson().get("type").asText() : // diff --git a/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/assist/JsonReferenceProposalProvider.java b/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/assist/JsonReferenceProposalProvider.java index 1654b170..a109869e 100644 --- a/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/assist/JsonReferenceProposalProvider.java +++ b/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/assist/JsonReferenceProposalProvider.java @@ -20,9 +20,8 @@ import com.google.common.collect.Lists; import com.reprezen.swagedit.core.assist.contexts.ContextType; import com.reprezen.swagedit.core.assist.contexts.ContextTypeCollection; -import com.reprezen.swagedit.core.editor.JsonDocument; +import com.reprezen.swagedit.core.json.JsonModel; import com.reprezen.swagedit.core.json.references.JsonDocumentManager; -import com.reprezen.swagedit.core.model.Model; import com.reprezen.swagedit.core.utils.DocumentUtils; import com.reprezen.swagedit.core.utils.SwaggerFileFinder; import com.reprezen.swagedit.core.utils.SwaggerFileFinder.Scope; @@ -49,8 +48,8 @@ protected ContextTypeCollection getContextTypes() { return contextTypes; } - public boolean canProvideProposal(Model model, JsonPointer pointer) { - return pointer != null && contextTypes.get(model, pointer) != ContextType.UNKNOWN; + public boolean canProvideProposal(JsonModel doc, JsonPointer pointer) { + return pointer != null && contextTypes.get(doc, pointer) != ContextType.UNKNOWN; } /** @@ -69,8 +68,8 @@ public boolean canProvideProposal(Model model, JsonPointer pointer) { * @param scope * @return proposals */ - public Collection getProposals(JsonPointer pointer, JsonDocument document, Scope scope) { - final ContextType type = contextTypes.get(document.getModel(), pointer); + public Collection getProposals(JsonPointer pointer, JsonModel document, Scope scope) { + final ContextType type = contextTypes.get(document, pointer); final IFile currentFile = getActiveFile(); final IPath basePath = currentFile.getParent().getFullPath(); final List proposals = Lists.newArrayList(); diff --git a/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/assist/contexts/ComponentContextType.java b/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/assist/contexts/ComponentContextType.java index 656005bb..64b1a71b 100644 --- a/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/assist/contexts/ComponentContextType.java +++ b/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/assist/contexts/ComponentContextType.java @@ -14,9 +14,8 @@ import com.fasterxml.jackson.core.JsonPointer; import com.fasterxml.jackson.databind.JsonNode; +import com.reprezen.swagedit.core.json.JsonModel; import com.reprezen.swagedit.core.json.references.JsonReference; -import com.reprezen.swagedit.core.model.AbstractNode; -import com.reprezen.swagedit.core.model.Model; import com.reprezen.swagedit.core.schema.ComplexTypeDefinition; import com.reprezen.swagedit.core.schema.MultipleTypeDefinition; import com.reprezen.swagedit.core.schema.TypeDefinition; @@ -35,20 +34,20 @@ protected String getReferencePointerString() { } @Override - public boolean canProvideProposal(Model model, JsonPointer pointer) { - if (model == null) { + public boolean canProvideProposal(JsonModel document, JsonPointer pointer) { + if (document == null) { // model can be null when initTextMessages called in new JsonContentAssistProcessor() return false; } - return isReference(model, pointer) && isReferenceToComponent(model, pointer); + return isReference(document, pointer) && isReferenceToComponent(document, pointer); } - protected boolean isReference(Model model, JsonPointer pointer) { - AbstractNode contextNode = model.find(pointer); + protected boolean isReference(JsonModel document, JsonPointer pointer) { + JsonNode contextNode = document.getContent().at(pointer); if (contextNode == null) { return false; } - TypeDefinition type = contextNode.getType(); + TypeDefinition type = document.getTypes().get(pointer); if (type instanceof MultipleTypeDefinition) { // MultipleTypeDefinition is a special case, it happens when several properties match a property for (TypeDefinition nestedType : ((MultipleTypeDefinition) type).getMultipleTypes()) { @@ -64,12 +63,8 @@ protected boolean isReference(Model model, JsonPointer pointer) { return getReferencePointerString().equals(pointerToType.toString()); } - protected boolean isReferenceToComponent(Model model, JsonPointer pointer) { - AbstractNode parentNode = model.find(pointer.head()); - if (parentNode == null) { - return false; - } - TypeDefinition parentType = parentNode.getType(); + protected boolean isReferenceToComponent(JsonModel document, JsonPointer pointer) { + TypeDefinition parentType = document.getTypes().get(pointer.head()); if (parentType instanceof ComplexTypeDefinition) { Collection types = ((ComplexTypeDefinition) parentType).getComplexTypes(); for (TypeDefinition type : types) { diff --git a/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/assist/contexts/ContextType.java b/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/assist/contexts/ContextType.java index 8849a968..e30be76c 100644 --- a/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/assist/contexts/ContextType.java +++ b/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/assist/contexts/ContextType.java @@ -20,10 +20,8 @@ import com.fasterxml.jackson.databind.JsonNode; import com.google.common.collect.Lists; import com.reprezen.swagedit.core.assist.Proposal; -import com.reprezen.swagedit.core.editor.JsonDocument; -import com.reprezen.swagedit.core.model.Model; +import com.reprezen.swagedit.core.json.JsonModel; import com.reprezen.swagedit.core.utils.URLUtils; -import com.reprezen.swagedit.core.validation.ValidationUtil; /** * Represents the different contexts for which a JSON reference may be computed.
@@ -32,7 +30,7 @@ public abstract class ContextType { public static final ContextType UNKNOWN = new ContextType(null, "") { @Override - public boolean canProvideProposal(Model model, JsonPointer pointer) { + public boolean canProvideProposal(JsonModel document, JsonPointer pointer) { return false; } }; @@ -53,7 +51,7 @@ public ContextType(String value, String label, boolean isLocalOnly) { this.isLocalOnly = isLocalOnly; } - public abstract boolean canProvideProposal(Model model, JsonPointer pointer); + public abstract boolean canProvideProposal(JsonModel document, JsonPointer pointer); public String value() { return value; @@ -67,8 +65,8 @@ public boolean isLocalOnly() { return isLocalOnly; } - public Collection collectProposals(JsonDocument document, IPath path) { - return collectProposals(document.asJson(), path); + public Collection collectProposals(JsonModel document, IPath path) { + return collectProposals(document.getContent(), path); } /** @@ -85,7 +83,10 @@ public Collection collectProposals(JsonNode document, IPath path) { return results; } - final JsonNode nodes = ValidationUtil.findNode(value(), document); + System.out.println(value() + " " + this.getClass().getName()); + System.out.println(document); + final JsonNode nodes = document.at(value()); + System.out.println("HERE " + nodes); if (nodes == null) { return results; } diff --git a/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/assist/contexts/ContextTypeCollection.java b/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/assist/contexts/ContextTypeCollection.java index f8995a28..06a77f5a 100644 --- a/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/assist/contexts/ContextTypeCollection.java +++ b/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/assist/contexts/ContextTypeCollection.java @@ -10,7 +10,7 @@ *******************************************************************************/ package com.reprezen.swagedit.core.assist.contexts; import com.fasterxml.jackson.core.JsonPointer; -import com.reprezen.swagedit.core.model.Model; +import com.reprezen.swagedit.core.json.JsonModel; public class ContextTypeCollection { @@ -20,9 +20,9 @@ protected ContextTypeCollection(Iterable contextTypes) { this.contextTypes = contextTypes; } - public ContextType get(Model model, JsonPointer pointer) { + public ContextType get(JsonModel document, JsonPointer pointer) { for (ContextType next : contextTypes) { - if (next.canProvideProposal(model, pointer)) { + if (next.canProvideProposal(document, pointer)) { return next; } } diff --git a/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/assist/contexts/RegexContextType.java b/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/assist/contexts/RegexContextType.java index f943c87c..2c3affd2 100644 --- a/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/assist/contexts/RegexContextType.java +++ b/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/assist/contexts/RegexContextType.java @@ -11,7 +11,7 @@ package com.reprezen.swagedit.core.assist.contexts; import com.fasterxml.jackson.core.JsonPointer; -import com.reprezen.swagedit.core.model.Model; +import com.reprezen.swagedit.core.json.JsonModel; /** * @@ -31,7 +31,8 @@ public RegexContextType(String value, String label, String regex, boolean isLoca this.regex = regex; } - public boolean canProvideProposal(Model model, JsonPointer pointer) { + @Override + public boolean canProvideProposal(JsonModel document, JsonPointer pointer) { if (pointer != null && regex != null) { return pointer.toString().matches(regex); } diff --git a/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/assist/contexts/SchemaContextType.java b/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/assist/contexts/SchemaContextType.java index ff896a7b..dc06c77c 100644 --- a/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/assist/contexts/SchemaContextType.java +++ b/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/assist/contexts/SchemaContextType.java @@ -10,14 +10,6 @@ *******************************************************************************/ package com.reprezen.swagedit.core.assist.contexts; -import java.util.Collection; - -import org.eclipse.core.runtime.IPath; - -import com.fasterxml.jackson.databind.JsonNode; -import com.reprezen.swagedit.core.assist.Proposal; -import com.reprezen.swagedit.core.editor.JsonDocument; -import com.reprezen.swagedit.core.model.Model; import com.reprezen.swagedit.core.schema.CompositeSchema; public abstract class SchemaContextType extends RegexContextType { @@ -37,17 +29,5 @@ public SchemaContextType(CompositeSchema schema, String value, String label, Str public CompositeSchema getSchema() { return schema; } - - @Override - public Collection collectProposals(JsonDocument document, IPath path) { - return collectProposals(document.getModel(), path); - } - - @Override - public Collection collectProposals(JsonNode document, IPath path) { - return collectProposals(Model.parse(getSchema(), document), path); - } - - public abstract Collection collectProposals(Model parse, IPath path); } diff --git a/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/assist/ext/ContentAssistExt.java b/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/assist/ext/ContentAssistExt.java index 7c1c1e60..9bf042df 100644 --- a/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/assist/ext/ContentAssistExt.java +++ b/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/assist/ext/ContentAssistExt.java @@ -12,8 +12,8 @@ import java.util.Collection; +import com.fasterxml.jackson.databind.JsonNode; import com.reprezen.swagedit.core.assist.Proposal; -import com.reprezen.swagedit.core.model.AbstractNode; import com.reprezen.swagedit.core.schema.TypeDefinition; /** @@ -38,6 +38,6 @@ public interface ContentAssistExt { * @param prefix * @return proposals */ - Collection getProposals(TypeDefinition type, AbstractNode node, String prefix); + Collection getProposals(TypeDefinition type, JsonNode node, String prefix); } diff --git a/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/assist/ext/MediaTypeContentAssistExt.java b/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/assist/ext/MediaTypeContentAssistExt.java index 4a501c58..34f30b1a 100644 --- a/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/assist/ext/MediaTypeContentAssistExt.java +++ b/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/assist/ext/MediaTypeContentAssistExt.java @@ -23,7 +23,6 @@ import com.google.common.collect.Lists; import com.reprezen.swagedit.core.Activator; import com.reprezen.swagedit.core.assist.Proposal; -import com.reprezen.swagedit.core.model.AbstractNode; import com.reprezen.swagedit.core.schema.ArrayTypeDefinition; import com.reprezen.swagedit.core.schema.TypeDefinition; @@ -65,7 +64,7 @@ public boolean canProvideContentAssist(TypeDefinition type) { } @Override - public Collection getProposals(TypeDefinition type, AbstractNode node, String prefix) { + public Collection getProposals(TypeDefinition type, JsonNode node, String prefix) { Collection proposals = new ArrayList<>(); prefix = Strings.emptyToNull(prefix); diff --git a/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/assist/ext/ResponseCodeContentAssistExt.java b/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/assist/ext/ResponseCodeContentAssistExt.java index 296f9e1a..36d30d07 100644 --- a/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/assist/ext/ResponseCodeContentAssistExt.java +++ b/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/assist/ext/ResponseCodeContentAssistExt.java @@ -26,7 +26,6 @@ import com.google.common.collect.Lists; import com.reprezen.swagedit.core.Activator; import com.reprezen.swagedit.core.assist.Proposal; -import com.reprezen.swagedit.core.model.AbstractNode; import com.reprezen.swagedit.core.schema.TypeDefinition; public class ResponseCodeContentAssistExt implements ContentAssistExt { @@ -60,7 +59,7 @@ public boolean canProvideContentAssist(TypeDefinition type) { } @Override - public Collection getProposals(TypeDefinition type, AbstractNode node, String prefix) { + public Collection getProposals(TypeDefinition type, JsonNode node, String prefix) { Collection proposals = Lists.newArrayList(); for (Iterator it = statusCodes(prefix); it.hasNext();) { diff --git a/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/editor/JsonDocument.java b/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/editor/JsonDocument.java index b4f568d5..9265a010 100644 --- a/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/editor/JsonDocument.java +++ b/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/editor/JsonDocument.java @@ -11,22 +11,18 @@ package com.reprezen.swagedit.core.editor; import java.io.IOException; -import java.io.StringReader; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.Document; import org.eclipse.jface.text.IRegion; import org.eclipse.jface.text.Position; import org.eclipse.jface.text.Region; -import org.yaml.snakeyaml.Yaml; -import org.yaml.snakeyaml.nodes.Node; import org.yaml.snakeyaml.parser.ParserException; import com.fasterxml.jackson.core.JsonPointer; import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.reprezen.swagedit.core.model.AbstractNode; -import com.reprezen.swagedit.core.model.Model; +import com.reprezen.swagedit.core.json.JsonModel; +import com.reprezen.swagedit.core.json.JsonRegion; import com.reprezen.swagedit.core.schema.CompositeSchema; /** @@ -34,44 +30,16 @@ * */ public class JsonDocument extends Document { - - private final ObjectMapper mapper; - private CompositeSchema schema; - - - private final Yaml yaml = new Yaml(); - private JsonNode jsonContent; - private Node yamlContent; + private CompositeSchema schema; + private JsonModel model; - private Exception yamlError; - private Exception jsonError; - private Model model; - - public JsonDocument(ObjectMapper mapper, CompositeSchema schema) { - this.mapper = mapper; + public JsonDocument(CompositeSchema schema) { this.schema = schema; } - public Exception getYamlError() { - return yamlError; - } - - public Exception getJsonError() { - return jsonError; - } - - /** - * Returns YAML abstract representation of the document. - * - * @return Node - */ - public Node getYaml() { - if (yamlContent == null) { - parseYaml(get()); - } - - return yamlContent; + public JsonModel getContent() { + return model; } /** @@ -84,13 +52,9 @@ public Node getYaml() { * @throws IOException */ public JsonNode asJson() { - if (jsonContent == null) { - parseJson(get()); - } - - return jsonContent; + return model != null ? model.getContent() : null; } - + public CompositeSchema getSchema() { return schema; } @@ -126,78 +90,31 @@ public int getDelimiterPosition(int offset) { } public void onChange() { - final String content = get(); - - parseModel(); - parseYaml(content); - - // No need to parse json if - // there is already a yaml parsing error. - if (yamlError != null) { - jsonContent = null; - } else { - parseJson(content); - } - } - - private void parseYaml(String content) { try { - yamlContent = yaml.compose(new StringReader(content)); - yamlError = null; - } catch (Exception e) { - yamlContent = null; - yamlError = e; - } - } - - private void parseJson(String content) { - try { - Object expandedYamlObject = new Yaml().load(content); - jsonContent = mapper.valueToTree(expandedYamlObject); - jsonError = null; - } catch (Exception e) { - jsonContent = null; - jsonError = e; - } - } - - private void parseModel() { - try { - model = Model.parseYaml(schema, get()); - } catch (Exception e) { + model = new JsonModel(schema, get(), true); + } catch (IOException e) { + // TODO Auto-generated catch block model = null; } } - /** - * @return the Model, or null if the spec is invalid YAML - */ - public Model getModel() { - if (model == null) { - parseModel(); - } - return model; - } - - public Model getModel(int offset) { - // no parse errors - if (model != null) { - return model; - } - + public JsonPointer getPath(int line, int column) { + System.out.println("GET PATH " + get()); + JsonModel model; try { - if (0 > offset || offset > getLength()) { - return Model.parseYaml(schema, get()); - } else { - return Model.parseYaml(schema, get(0, offset)); - } - } catch (BadLocationException e) { + model = new JsonModel(schema, get(), false); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); return null; } + JsonRegion range = model.findRegion(line, column); + + return getPointer(range); } - public JsonPointer getPath(int line, int column) { - return getModel().getPath(line, column); + private JsonPointer getPointer(JsonRegion range) { + return JsonPointer.compile(range.pointer.toString()); } public JsonPointer getPath(IRegion region) { @@ -208,7 +125,7 @@ public JsonPointer getPath(IRegion region) { return null; } - return getModel().getPath(lineOfOffset, getColumnOfOffset(lineOfOffset, region)); + return getPath(lineOfOffset + 1, getColumnOfOffset(lineOfOffset, region) + 1); } public int getColumnOfOffset(int line, IRegion region) { @@ -223,17 +140,13 @@ public int getColumnOfOffset(int line, IRegion region) { } public IRegion getRegion(JsonPointer pointer) { - Model model = getModel(); - if (model == null) { - return null; - } + JsonRegion range = model.getRegion(pointer); - AbstractNode node = model.find(pointer); - if (node == null) { + if (range == null) { return new Region(0, 0); } - Position position = node.getPosition(this); + Position position = range.getPosition(this); return new Region(position.getOffset(), position.getLength()); } diff --git a/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/editor/JsonEditor.java b/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/editor/JsonEditor.java index 3325cc16..857bdc4c 100755 --- a/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/editor/JsonEditor.java +++ b/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/editor/JsonEditor.java @@ -74,12 +74,9 @@ import org.eclipse.ui.swt.IFocusService; import org.eclipse.ui.texteditor.IDocumentProvider; import org.eclipse.ui.views.contentoutline.IContentOutlinePage; -import org.yaml.snakeyaml.error.YAMLException; -import com.fasterxml.jackson.core.JsonProcessingException; import com.reprezen.swagedit.core.editor.outline.JsonContentOutlinePage; import com.reprezen.swagedit.core.handlers.OpenQuickOutlineHandler; -import com.reprezen.swagedit.core.model.AbstractNode; import com.reprezen.swagedit.core.validation.SwaggerError; import com.reprezen.swagedit.core.validation.Validator; @@ -448,7 +445,6 @@ private void validate(boolean onOpen) { ((JsonDocument) document).onChange(); } clearMarkers(file); - validateYaml(file, (JsonDocument) document); validateSwagger(file, (JsonDocument) document, fileEditorInput); } } @@ -463,15 +459,6 @@ protected void clearMarkers(IFile file) { } } - protected void validateYaml(IFile file, JsonDocument document) { - if (document.getYamlError() instanceof YAMLException) { - addMarker(SwaggerError.newYamlError((YAMLException) document.getYamlError()), file, document); - } - if (document.getJsonError() instanceof JsonProcessingException) { - addMarker(SwaggerError.newJsonError((JsonProcessingException) document.getJsonError()), file, document); - } - } - protected void validateSwagger(IFile file, JsonDocument document, IFileEditorInput editorInput) { final Set errors = validator.validate(document, editorInput); @@ -555,11 +542,12 @@ public boolean show(ShowInContext context) { if (selection instanceof IStructuredSelection) { Object selected = ((IStructuredSelection) selection).getFirstElement(); - if (selected instanceof AbstractNode) { - Position position = ((AbstractNode) selected).getPosition(getSourceViewer().getDocument()); - selectAndReveal(position.getOffset(), position.getLength()); - return true; - } + // TODO + // if (selected instanceof AbstractNode) { + // Position position = ((AbstractNode) selected).getPosition(getSourceViewer().getDocument()); + // selectAndReveal(position.getOffset(), position.getLength()); + // return true; + // } } return false; diff --git a/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/editor/JsonReconcilingStrategy.java b/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/editor/JsonReconcilingStrategy.java index 1b476db4..ea838afe 100644 --- a/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/editor/JsonReconcilingStrategy.java +++ b/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/editor/JsonReconcilingStrategy.java @@ -21,9 +21,7 @@ import org.eclipse.jface.text.reconciler.DirtyRegion; import org.eclipse.jface.text.reconciler.IReconcilingStrategy; import org.eclipse.jface.text.reconciler.IReconcilingStrategyExtension; -import org.eclipse.swt.widgets.Display; import org.yaml.snakeyaml.nodes.MappingNode; -import org.yaml.snakeyaml.nodes.Node; import org.yaml.snakeyaml.nodes.NodeTuple; public class JsonReconcilingStrategy implements IReconcilingStrategy, IReconcilingStrategyExtension { @@ -62,16 +60,16 @@ protected void calculatePositions() { if (!(document instanceof JsonDocument)) return; - final Node yaml = ((JsonDocument) document).getYaml(); - if (!(yaml instanceof MappingNode)) { - return; - } - - Display.getDefault().asyncExec(new Runnable() { - public void run() { - editor.updateFoldingStructure(calculatePositions((MappingNode) yaml)); - } - }); + // final Node yaml = ((JsonDocument) document).getYaml(); + // if (!(yaml instanceof MappingNode)) { + // return; + // } + // + // Display.getDefault().asyncExec(new Runnable() { + // public void run() { + // editor.updateFoldingStructure(calculatePositions((MappingNode) yaml)); + // } + // }); } protected List calculatePositions(MappingNode mapping) { diff --git a/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/editor/JsonSourceViewerConfiguration.java b/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/editor/JsonSourceViewerConfiguration.java index 9a779336..7a585e56 100644 --- a/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/editor/JsonSourceViewerConfiguration.java +++ b/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/editor/JsonSourceViewerConfiguration.java @@ -40,7 +40,6 @@ import com.reprezen.swagedit.core.assist.JsonContentAssistProcessor; import com.reprezen.swagedit.core.assist.JsonQuickAssistProcessor; import com.reprezen.swagedit.core.editor.outline.QuickOutline; -import com.reprezen.swagedit.core.model.Model; import com.reprezen.swagedit.core.quickfix.QuickFixer; import com.reprezen.swagedit.core.schema.CompositeSchema; @@ -93,7 +92,7 @@ protected ContextTypeRegistry getContextTypeRegistry() { } @Override - protected String getContextTypeId(Model model, String path) { + protected String getContextTypeId(JsonDocument doc, String path) { return null; }}; } @@ -199,7 +198,7 @@ public Object getInformation2(ITextViewer textViewer, IRegion subject) { IDocument document = textViewer.getDocument(); if (document instanceof JsonDocument) { - return ((JsonDocument) document).getModel(); + return ((JsonDocument) document).asJson(); } return null; diff --git a/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/editor/outline/JsonContentOutlinePage.java b/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/editor/outline/JsonContentOutlinePage.java index 7491c9ef..ca62a4f8 100644 --- a/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/editor/outline/JsonContentOutlinePage.java +++ b/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/editor/outline/JsonContentOutlinePage.java @@ -10,9 +10,6 @@ *******************************************************************************/ package com.reprezen.swagedit.core.editor.outline; -import static com.google.common.collect.Iterables.filter; -import static com.google.common.collect.Iterables.toArray; - import java.util.Arrays; import java.util.List; @@ -25,10 +22,8 @@ import org.eclipse.ui.texteditor.IDocumentProvider; import org.eclipse.ui.views.contentoutline.ContentOutlinePage; -import com.google.common.base.Predicate; import com.reprezen.swagedit.core.editor.JsonDocument; -import com.reprezen.swagedit.core.model.AbstractNode; -import com.reprezen.swagedit.core.model.Model; +import com.reprezen.swagedit.core.json.JsonModel; public class JsonContentOutlinePage extends ContentOutlinePage { @@ -75,7 +70,7 @@ protected void update() { final IDocument document = documentProvider.getDocument(currentInput); if (document instanceof JsonDocument) { - final Model model = ((JsonDocument) document).getModel(); + JsonModel model = ((JsonDocument) document).getContent(); if (model == null) { return; } @@ -91,13 +86,13 @@ protected void update() { viewer.setInput(model); if (elements != null && !elements.isEmpty()) { - Iterable newElements = filter(model.allNodes(), new Predicate() { - @Override - public boolean apply(AbstractNode node) { - return elements.contains(node); - } - }); - viewer.setExpandedElements(toArray(newElements, AbstractNode.class)); + // Iterable newElements = filter(model.allNodes(), new Predicate() { + // @Override + // public boolean apply(AbstractNode node) { + // return elements.contains(node); + // } + // }); + // viewer.setExpandedElements(toArray(newElements, AbstractNode.class)); } } } diff --git a/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/editor/outline/OutlineContentProvider.java b/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/editor/outline/OutlineContentProvider.java index 457d838a..8bd3f5f6 100644 --- a/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/editor/outline/OutlineContentProvider.java +++ b/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/editor/outline/OutlineContentProvider.java @@ -11,18 +11,20 @@ package com.reprezen.swagedit.core.editor.outline; import java.util.List; +import java.util.Set; +import org.apache.commons.lang3.tuple.Pair; import org.eclipse.jface.viewers.ITreeContentProvider; import org.eclipse.jface.viewers.Viewer; +import com.fasterxml.jackson.core.JsonPointer; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; -import com.reprezen.swagedit.core.model.AbstractNode; -import com.reprezen.swagedit.core.model.Model; +import com.reprezen.swagedit.core.json.JsonModel; public class OutlineContentProvider implements ITreeContentProvider { - private Iterable models; + private Iterable models; @Override public void dispose() { @@ -34,10 +36,10 @@ public void dispose() { public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { if (newInput == null) { this.models = Lists.newArrayList(); - } else if (newInput instanceof Model) { - this.models = Lists.newArrayList((Model) newInput); + } else if (newInput instanceof JsonModel) { + this.models = Lists.newArrayList((JsonModel) newInput); } else if (Iterable.class.isAssignableFrom(newInput.getClass())) { - this.models = (Iterable) newInput; + this.models = (Iterable) newInput; } } @@ -47,34 +49,47 @@ public Object[] getElements(Object inputElement) { return null; } - List roots = Lists.newArrayList(); - for (Model model : models) { - roots.add(model.getRoot()); + List> roots = Lists.newArrayList(); + for (JsonModel model : models) { + roots.add(Pair.of(model, JsonPointer.compile(""))); } return roots.toArray(new Object[roots.size()]); } + @SuppressWarnings("unchecked") @Override public Object[] getChildren(Object parentElement) { - if (parentElement instanceof AbstractNode) { - return Iterables.toArray(((AbstractNode) parentElement).elements(), AbstractNode.class); + if (parentElement instanceof Pair) { + JsonModel model = ((Pair) parentElement).getLeft(); + JsonPointer ptr = ((Pair) parentElement).getRight(); + + List> result = Lists.newArrayList(); + Set children = model.getPaths().get(ptr); + if (children != null) { + for (JsonPointer p : children) { + result.add(Pair.of(model, p)); + } + } + return result.toArray(); } return null; } @Override public Object getParent(Object element) { - if (element instanceof AbstractNode) { - return ((AbstractNode) element).getParent(); - } return null; } + @SuppressWarnings("unchecked") @Override public boolean hasChildren(Object element) { - if (element instanceof AbstractNode) { - return !Iterables.isEmpty(((AbstractNode) element).elements()); + if (element instanceof Pair) { + JsonModel model = ((Pair) element).getLeft(); + JsonPointer ptr = ((Pair) element).getRight(); + + Set children = model.getPaths().get(ptr); + return children != null && !children.isEmpty(); } return false; } diff --git a/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/editor/outline/OutlineStyledLabelProvider.java b/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/editor/outline/OutlineStyledLabelProvider.java index 4af7516e..3dbc0e8d 100644 --- a/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/editor/outline/OutlineStyledLabelProvider.java +++ b/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/editor/outline/OutlineStyledLabelProvider.java @@ -10,6 +10,7 @@ *******************************************************************************/ package com.reprezen.swagedit.core.editor.outline; +import org.apache.commons.lang3.tuple.Pair; import org.eclipse.jface.viewers.StyledCellLabelProvider; import org.eclipse.jface.viewers.StyledString; import org.eclipse.jface.viewers.StyledString.Styler; @@ -20,10 +21,11 @@ import org.eclipse.swt.graphics.TextStyle; import org.eclipse.swt.widgets.Display; -import com.google.common.collect.Iterables; +import com.fasterxml.jackson.core.JsonPointer; +import com.fasterxml.jackson.databind.JsonNode; import com.reprezen.swagedit.core.Activator; import com.reprezen.swagedit.core.Activator.Icons; -import com.reprezen.swagedit.core.model.AbstractNode; +import com.reprezen.swagedit.core.json.JsonModel; import com.reprezen.swagedit.core.schema.TypeDefinition; public class OutlineStyledLabelProvider extends StyledCellLabelProvider { @@ -55,25 +57,32 @@ public Styler getTextStyler() { return TEXT_STYLER; } + @SuppressWarnings("unchecked") @Override public void update(ViewerCell cell) { Object element = cell.getElement(); - if (element instanceof AbstractNode) { - StyledString styledString = getStyledString((AbstractNode) element); + if (element instanceof Pair) { + JsonModel model = ((Pair) element).getLeft(); + JsonPointer ptr = ((Pair) element).getRight(); + + StyledString styledString = getStyledString(model, ptr); cell.setText(styledString.toString()); cell.setStyleRanges(styledString.getStyleRanges()); - cell.setImage(getImage(getIcon((AbstractNode) element))); + cell.setImage(getImage(getIcon(model, ptr))); } } - public StyledString getStyledString(AbstractNode element) { - StyledString styledString = new StyledString(element.getText(), getTextStyler()); + public StyledString getStyledString(JsonModel model, JsonPointer ptr) { + JsonNode node = model.getContent().at(ptr); + JsonNode parent = ptr.head() == null ? null : model.getContent().at(ptr.head()); + + StyledString styledString = new StyledString(getText(node, ptr), getTextStyler()); - if (element.getParent() != null && (element.isObject() || element.isArray())) { + if (parent != null && (node.isObject() || node.isArray())) { - TypeDefinition definition = element.getType(); + TypeDefinition definition = model.getTypes().get(ptr); String label = null; if (definition != null && definition.asJson() != null) { @@ -89,32 +98,39 @@ public StyledString getStyledString(AbstractNode element) { styledString.append(label, getTagStyler()); } - } else if (element.getParent() == null) { - - if (element.getModel().getPath() != null) { + } else if (parent == null) { + if (model.getPath() != null) { styledString.append(" "); - styledString.append(element.getModel().getPath().toString(), getTagStyler()); + styledString.append(model.getPath().toString(), getTagStyler()); } } - return styledString; } - protected Icons getIcon(AbstractNode element) { - AbstractNode parent = element.getParent(); + public String getText(JsonNode node, JsonPointer ptr) { + if (node.isObject() || node.isArray()) { + return ptr.getMatchingProperty(); + } else { + return ptr.getMatchingProperty() + " : " + node.asText(); + } + } + + protected Icons getIcon(JsonModel model, JsonPointer ptr) { + JsonNode parent = ptr.head() == null ? null : model.getContent().at(ptr.head()); + JsonNode node = model.getContent().at(ptr); if (parent == null) { return Icons.outline_document; } if (parent.isObject()) { - if (Iterables.isEmpty(element.elements())) { + if (!node.elements().hasNext()) { return Icons.outline_mapping_scalar; } else { return Icons.outline_mapping; } } else if (parent.isArray()) { - if (Iterables.isEmpty(element.elements())) { + if (!node.elements().hasNext()) { return Icons.outline_scalar; } else { return Icons.outline_sequence; diff --git a/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/editor/outline/QuickOutline.java b/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/editor/outline/QuickOutline.java index 996aa29d..18314096 100644 --- a/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/editor/outline/QuickOutline.java +++ b/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/editor/outline/QuickOutline.java @@ -51,15 +51,13 @@ import org.eclipse.ui.IFileEditorInput; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.keys.IBindingService; -import org.eclipse.ui.part.ShowInContext; +import com.fasterxml.jackson.databind.JsonNode; import com.google.common.base.Strings; import com.reprezen.swagedit.core.Messages; import com.reprezen.swagedit.core.editor.JsonEditor; -import com.reprezen.swagedit.core.model.AbstractNode; -import com.reprezen.swagedit.core.model.Model; +import com.reprezen.swagedit.core.json.JsonModel; import com.reprezen.swagedit.core.schema.CompositeSchema; -import com.reprezen.swagedit.core.utils.DocumentUtils; import com.reprezen.swagedit.core.utils.SwaggerFileFinder; import com.reprezen.swagedit.core.utils.SwaggerFileFinder.Scope; @@ -190,7 +188,8 @@ protected void handleMultiView() { treeViewer.setAutoExpandLevel(0); } - setInput(Model.parseYaml(files, getSchema())); + // TODO + // setInput(Model.parseYaml(files, getSchema())); } protected String statusMessage() { @@ -284,14 +283,15 @@ protected void handleSelection() { if (selection != null) { Object element = selection.getFirstElement(); - if (element instanceof AbstractNode) { - Model model = ((AbstractNode) element).getModel(); - - if (model.getPath() != null) { - DocumentUtils.openAndReveal(model.getPath(), selection); - } else { - editor.show(new ShowInContext(null, selection)); - } + if (element instanceof JsonNode) { + // TODO + // Model model = ((AbstractNode) element).getModel(); + // + // if (model.getPath() != null) { + // DocumentUtils.openAndReveal(model.getPath(), selection); + // } else { + // editor.show(new ShowInContext(null, selection)); + // } } } } @@ -304,19 +304,20 @@ protected Control createDialogArea(Composite parent) { @Override public void setInput(Object input) { - if (input instanceof Model) { - Model model = (Model) input; - if (model.getPath() == null) { - IFile currentFile = null; - IEditorInput editorInput = editor.getEditorInput(); - - if (editorInput instanceof IFileEditorInput) { - currentFile = ((IFileEditorInput) editorInput).getFile(); - } - if (currentFile != null) { - model.setPath(currentFile.getFullPath()); - } - } + if (input instanceof JsonModel) { + JsonModel model = (JsonModel) input; + // TODO + // if (model.getPath() == null) { + // IFile currentFile = null; + // IEditorInput editorInput = editor.getEditorInput(); + // + // if (editorInput instanceof IFileEditorInput) { + // currentFile = ((IFileEditorInput) editorInput).getFile(); + // } + // if (currentFile != null) { + // model.setPath(currentFile.getFullPath()); + // } + // } } treeViewer.setInput(input); @@ -418,8 +419,8 @@ private boolean matchesFilter(Object element) { return true; } - if (element instanceof AbstractNode) { - String matchName = ((AbstractNode) element).getText(); + if (element instanceof JsonNode) { + String matchName = ((JsonNode) element).asText(); String text = filterText.getText(); if (Strings.emptyToNull(text) == null) { diff --git a/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/hyperlinks/DefinitionHyperlinkDetector.java b/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/hyperlinks/DefinitionHyperlinkDetector.java index 2ed42838..b8922e40 100644 --- a/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/hyperlinks/DefinitionHyperlinkDetector.java +++ b/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/hyperlinks/DefinitionHyperlinkDetector.java @@ -20,8 +20,8 @@ import org.eclipse.jface.text.hyperlink.IHyperlink; import com.fasterxml.jackson.core.JsonPointer; +import com.fasterxml.jackson.databind.JsonNode; import com.reprezen.swagedit.core.editor.JsonDocument; -import com.reprezen.swagedit.core.model.AbstractNode; /** * Hyperlink detector that detects links to and inside schema definition elements. @@ -71,18 +71,20 @@ protected JsonPointer getRequiredPropertyPath(JsonDocument doc, HyperlinkInfo in return null; } - AbstractNode container = doc.getModel().find(JsonPointer.compile(containerPath)); + JsonNode container = doc.asJson().at(JsonPointer.compile(containerPath)); if (container.get("properties") != null && container.get("properties").get(info.text) != null) { - return container.get("properties").get(info.text).getPointer(); + return JsonPointer.compile(containerPath) + .append(JsonPointer.compile("/" + container.get("properties").get(info.text).asText())); } else { return null; } } protected JsonPointer getTagDefinitionPath(JsonDocument doc, HyperlinkInfo info, JsonPointer pointer) { - AbstractNode node = doc.getModel().find(JsonPointer.compile("/definitions/" + info.text)); + JsonPointer ptr = JsonPointer.compile("/definitions/" + info.text); + JsonNode node = doc.asJson().at(ptr); - return node != null ? node.getPointer() : null; + return node != null ? ptr : null; } } diff --git a/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/hyperlinks/PathParamHyperlinkDetector.java b/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/hyperlinks/PathParamHyperlinkDetector.java index 358a27a2..01afa284 100644 --- a/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/hyperlinks/PathParamHyperlinkDetector.java +++ b/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/hyperlinks/PathParamHyperlinkDetector.java @@ -23,11 +23,11 @@ import org.eclipse.jface.text.hyperlink.IHyperlink; import com.fasterxml.jackson.core.JsonPointer; +import com.fasterxml.jackson.databind.JsonNode; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.reprezen.swagedit.core.editor.JsonDocument; import com.reprezen.swagedit.core.json.references.JsonReference; -import com.reprezen.swagedit.core.model.AbstractNode; /** * Hyperlink detector that detects links from path parameters. @@ -85,7 +85,7 @@ protected IHyperlink[] doDetect(JsonDocument doc, ITextViewer viewer, HyperlinkI private Map findParameterPath(JsonDocument doc, JsonPointer basePath, String parameter) { Map paths = Maps.newHashMap(); - AbstractNode parent = doc.getModel().find(basePath); + JsonNode parent = doc.asJson().at(basePath); if (parent == null || !parent.isObject()) { return paths; } @@ -95,25 +95,25 @@ private Map findParameterPath(JsonDocument doc, JsonPointer continue; } - AbstractNode parameters = parent.get(method).get("parameters"); + JsonNode parameters = parent.get(method).get("parameters"); if (parameters != null && parameters.isArray()) { for (int i = 0; i < parameters.size(); i++) { - AbstractNode current = parameters.get(i); + JsonNode current = parameters.get(i); if (JsonReference.isReference(current)) { - JsonPointer ptr = JsonReference.getPointer(current.asObject()); - AbstractNode resolved = doc.getModel().find(ptr); + JsonPointer ptr = JsonReference.getPointer(current); + JsonNode resolved = doc.asJson().at(ptr); if (resolved != null && resolved.isObject() && resolved.get("name") != null) { - if (parameter.equals(resolved.get("name").asValue().getValue())) { + if (parameter.equals(resolved.get("name").asText())) { paths.put(method, ptr); } } } else if (current.isObject() && current.get("name") != null) { - if (parameter.equals(current.get("name").asValue().getValue())) { + if (parameter.equals(current.get("name").asText())) { paths.put(method, JsonPointer.compile(basePath + "/" + method + "/parameters/" + i)); } } diff --git a/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/hyperlinks/ReferenceHyperlinkDetector.java b/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/hyperlinks/ReferenceHyperlinkDetector.java index d0231c15..45d1ea47 100644 --- a/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/hyperlinks/ReferenceHyperlinkDetector.java +++ b/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/hyperlinks/ReferenceHyperlinkDetector.java @@ -19,10 +19,10 @@ import org.eclipse.ui.part.FileEditorInput; import com.fasterxml.jackson.core.JsonPointer; +import com.fasterxml.jackson.databind.JsonNode; import com.reprezen.swagedit.core.editor.JsonDocument; import com.reprezen.swagedit.core.json.references.JsonReference; import com.reprezen.swagedit.core.json.references.JsonReferenceFactory; -import com.reprezen.swagedit.core.model.AbstractNode; import com.reprezen.swagedit.core.utils.DocumentUtils; public abstract class ReferenceHyperlinkDetector extends AbstractJsonHyperlinkDetector { @@ -39,8 +39,8 @@ protected abstract JsonFileHyperlink createFileHyperlink(IRegion linkRegion, Str protected IHyperlink[] doDetect(JsonDocument doc, ITextViewer viewer, HyperlinkInfo info, JsonPointer pointer) { URI baseURI = getBaseURI(); - AbstractNode node = doc.getModel().find(pointer); - JsonReference reference = getFactory().createSimpleReference(getBaseURI(), node); + JsonNode node = doc.asJson().at(pointer); + JsonReference reference = getFactory().createSimpleReference(getBaseURI(), doc.asJson(), node); if (reference == null) { reference = getFactory().create(node); } diff --git a/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/json/JsonModel.java b/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/json/JsonModel.java new file mode 100644 index 00000000..3ccd95b1 --- /dev/null +++ b/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/json/JsonModel.java @@ -0,0 +1,149 @@ +package com.reprezen.swagedit.core.json; + +import java.io.IOException; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import com.fasterxml.jackson.core.JsonPointer; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.base.Strings; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import com.reprezen.swagedit.core.schema.CompositeSchema; +import com.reprezen.swagedit.core.schema.TypeDefinition; +import com.reprezen.swagedit.core.validation.SwaggerError; + +public class JsonModel { + + private final static LineRecorderYamlFactory factory = new LineRecorderYamlFactory(); + private final static ObjectMapper mapper = new ObjectMapper(factory); + + private final List errors = Lists.newArrayList(); + private final Map types = Maps.newHashMap(); + private final Set references = Sets.newHashSet(); + + private Map regions = Maps.newHashMap(); + private Map> paths = Maps.newHashMap(); + + private final CompositeSchema schema; + private final JsonRegionLocator locator; + + private JsonRegion range = null; + private JsonNode content = null; + + public JsonModel(CompositeSchema schema, String text, boolean strict) throws IOException { + this.schema = schema; + + if (Strings.emptyToNull(text) == null) { + this.content = mapper.createObjectNode(); + this.regions.put(JsonPointer.compile(""), new JsonRegion(JsonPointer.compile(""))); + this.paths.put(JsonPointer.compile(""), Sets. newHashSet()); + } else { + LineRecorderYamlParser parser = (LineRecorderYamlParser) factory.createParser(text); + this.content = mapper.reader().readTree(parser); + this.regions = parser.getLines(); + this.paths = parser.getPaths(); + this.errors.addAll(parser.getErrors()); + } + + this.locator = new JsonRegionLocator(regions, paths); + + for (JsonPointer ptr : regions.keySet()) { + addTypeAndReferences(ptr); + } + } + + public List getErrors() { + return errors; + } + + public CompositeSchema getSchema() { + return schema; + } + + public Set getReferences() { + return references; + } + + public JsonNode getContent() { + return content; + } + + private void addTypeAndReferences(JsonPointer ptr) { + TypeDefinition type = schema.getType(ptr); + if (type != null) { + types.put(ptr, type); + } + + if (ptr.toString().endsWith("$ref")) { + references.add(ptr); + } + } + + public List findByType(JsonPointer typePointer) { + List found = Lists.newArrayList(); + for (JsonPointer ptr : types.keySet()) { + TypeDefinition type = types.get(ptr); + if (type.getPointer().equals(typePointer)) { + found.add(content.at(ptr)); + } + } + return found; + } + + public Map getTypes() { + return types; + } + + /** + * Returns the type definition if present for the node located at the given pointer. + * + * @param ptr + * @return type + */ + public TypeDefinition getType(JsonPointer ptr) { + return getTypes().get(ptr); + } + + public Map getRanges() { + return regions; + } + + public Map> getPaths() { + return paths; + } + + /** + * Returns the closest region that contains the location represented by a line and column. + * + * @param line + * @param column + * @return region + */ + public JsonRegion findRegion(int line, int column) { + return locator.findRegion(line, column); + } + + /** + * Returns the region of the given pointer. + * + * @param pointer + * @return region + */ + public JsonRegion getRegion(JsonPointer pointer) { + return pointer != null ? regions.get(pointer) : null; + } + + public void setPath() { + + } + + public Object getPath() { + // TODO Auto-generated method stub + return null; + } + +} diff --git a/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/json/JsonRegion.java b/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/json/JsonRegion.java new file mode 100644 index 00000000..2e9dd863 --- /dev/null +++ b/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/json/JsonRegion.java @@ -0,0 +1,114 @@ +package com.reprezen.swagedit.core.json; + +import java.util.Set; + +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.Position; + +import com.fasterxml.jackson.core.JsonPointer; +import com.google.common.collect.Sets; + +public class JsonRegion { + + public JsonPointer pointer; + + public static final class Location { + final public int startLine; + final public int startColumn; + final public int endLine; + final public int endColumn; + + Location(int startLine, int startColumn, int endLine, int endColumn) { + this.startLine = startLine; + this.startColumn = startColumn; + this.endLine = endLine; + this.endColumn = endColumn; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("Location ("); + builder.append(startLine); + builder.append(", "); + builder.append(startColumn); + builder.append("), ("); + builder.append(endLine); + builder.append(", "); + builder.append(endColumn); + builder.append(")"); + return builder.toString(); + } + + } + + private Location fieldLocation = null; + private Location contentLocation = new Location(1, 1, 1, 1); + + private final Set children = Sets.newHashSet(); + + public JsonRegion(JsonPointer pointer) { + this.pointer = pointer; + } + + public Location getFieldLocation() { + return fieldLocation; + } + + public void setFieldLocation(Location location) { + this.fieldLocation = location; + } + + public Location getContentLocation() { + return contentLocation; + } + + public void setContentLocation(Location location) { + this.contentLocation = location; + } + + public Set getChildren() { + return children; + } + + public Position getPosition(IDocument document) { + boolean selectEntireElement = false; + int startLine = contentLocation.startLine; + int offset = 0; + int length = 0; + + int endLine = contentLocation.endLine; + int endCol = contentLocation.endColumn; + try { + offset = document.getLineOffset(startLine); + if (selectEntireElement) { + length = (document.getLineOffset(endLine) + endCol) - offset; + } else if (startLine < document.getNumberOfLines() - 1) { + length = document.getLineOffset(startLine + 1) - offset; + } else { + length = document.getLineLength(startLine); + } + } catch (BadLocationException e) { + return new Position(0); + } + + return new Position(Math.max(0, offset), length); + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("RangeNode [pointer="); + builder.append(pointer); + builder.append(", fieldLocation="); + builder.append(fieldLocation); + builder.append(", contentLocation="); + builder.append(contentLocation); + builder.append(", children="); + builder.append(children); + builder.append("]"); + return builder.toString(); + } + +} diff --git a/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/json/JsonRegionLocator.java b/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/json/JsonRegionLocator.java new file mode 100644 index 00000000..1e7cf497 --- /dev/null +++ b/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/json/JsonRegionLocator.java @@ -0,0 +1,111 @@ +package com.reprezen.swagedit.core.json; + +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +import com.fasterxml.jackson.core.JsonPointer; +import com.reprezen.swagedit.core.json.JsonRegion.Location; + +public class JsonRegionLocator { + + private final Map regions; + private final Map> paths; + private final JsonRegion root; + + public JsonRegionLocator(Map regions, Map> paths) { + this.regions = regions; + this.paths = paths; + this.root = buildRangeTreeHelper(JsonPointer.compile("")); + } + + private JsonRegion buildRangeTreeHelper(JsonPointer pointer) { + JsonRegion range = regions.get(pointer); + if (range != null) { + Set pointers = paths.get(pointer); + if (pointers != null) { + for (JsonPointer p : pointers) { + if (!p.equals(pointer)) { + JsonRegion n = regions.get(p); + if (n != null) { + range.getChildren().add(n); + } + buildRangeTreeHelper(p); + } + } + } + + } + return range; + } + + public JsonRegion findRegion(int line, int column) { + if (column <= 1) { + return root; + } + + JsonRegion found = findContainingRegion(root.getChildren(), line, column); + if (found == null) { + found = findBeforeLine(root, line); + } + return found; + } + + private JsonRegion findBeforeLine(JsonRegion container, int line) { + JsonRegion found = null; + int previousLine = 0; + for (JsonRegion node : container.getChildren()) { + int l = node.getContentLocation().startLine; + if (l <= line && previousLine < l) { + found = node; + previousLine = l; + } + } + return found; + } + + private JsonRegion findContainingRegion(Collection ranges, int line, int column) { + JsonRegion contain = null; + Iterator it = ranges.iterator(); + while (it.hasNext() && contain == null) { + JsonRegion current = it.next(); + if (isInside(current, line)) { + if (column == current.getContentLocation().startColumn) { + contain = current; + } else { + JsonRegion inside = findContainingRegion(current.getChildren(), line, column); + if (inside != null) { + contain = inside; + } else { + if (column > current.getContentLocation().startColumn && !current.getChildren().isEmpty()) { + JsonRegion lastBeforeLine = findBeforeLine(current, line); + if (lastBeforeLine != null) { + contain = lastBeforeLine; + } + } else { + contain = current; + } + } + } + } + } + return contain; + } + + private boolean isInside(JsonRegion range, int line) { + Location start; + if (range.getFieldLocation() != null) { + start = range.getFieldLocation(); + } else { + start = range.getContentLocation(); + } + + return start.startLine <= line && line <= range.getContentLocation().endLine; + } + + public JsonRegion get(JsonPointer pointer) { + return regions.get(pointer); + } + +} \ No newline at end of file diff --git a/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/json/LineRecorderYamlFactory.java b/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/json/LineRecorderYamlFactory.java new file mode 100644 index 00000000..c86a062f --- /dev/null +++ b/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/json/LineRecorderYamlFactory.java @@ -0,0 +1,25 @@ +package com.reprezen.swagedit.core.json; + +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; + +import com.fasterxml.jackson.core.io.IOContext; +import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; +import com.fasterxml.jackson.dataformat.yaml.YAMLParser; + +public class LineRecorderYamlFactory extends YAMLFactory { + + @Override + protected YAMLParser _createParser(final InputStream in, final IOContext ctxt) throws IOException { + + return new LineRecorderYamlParser(ctxt, _getBufferRecycler(), _parserFeatures, _yamlParserFeatures, + _objectCodec, _createReader(in, null, ctxt)); + } + + @Override + protected YAMLParser _createParser(Reader r, IOContext ctxt) throws IOException { + return new LineRecorderYamlParser(ctxt, _getBufferRecycler(), _parserFeatures, _yamlParserFeatures, + _objectCodec, r); + } +} diff --git a/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/json/LineRecorderYamlParser.java b/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/json/LineRecorderYamlParser.java new file mode 100644 index 00000000..01df93f3 --- /dev/null +++ b/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/json/LineRecorderYamlParser.java @@ -0,0 +1,254 @@ +package com.reprezen.swagedit.core.json; + +import java.io.IOException; +import java.io.Reader; +import java.util.Map; +import java.util.Set; + +import org.eclipse.core.resources.IMarker; + +import com.fasterxml.jackson.core.JsonLocation; +import com.fasterxml.jackson.core.JsonPointer; +import com.fasterxml.jackson.core.JsonStreamContext; +import com.fasterxml.jackson.core.JsonToken; +import com.fasterxml.jackson.core.ObjectCodec; +import com.fasterxml.jackson.core.io.IOContext; +import com.fasterxml.jackson.core.util.BufferRecycler; +import com.fasterxml.jackson.dataformat.yaml.YAMLParser; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import com.reprezen.swagedit.core.json.JsonRegion.Location; +import com.reprezen.swagedit.core.validation.Messages; +import com.reprezen.swagedit.core.validation.SwaggerError; + +public class LineRecorderYamlParser extends YAMLParser { + private final Map ranges = Maps.newHashMap(); + private final Map> paths = Maps.newHashMap(); + private final Set errors = Sets.newHashSet(); + private final Set duplicateKeys = Sets.newHashSet(); + + private final Map arrayContent = Maps.newHashMap(); + + private JsonPointer ptr = JsonPointer.compile(""); + private boolean seenRoot = false; + private boolean strictMode; + + public LineRecorderYamlParser(IOContext ctxt, BufferRecycler br, int parserFeatures, int formatFeatures, + ObjectCodec codec, Reader reader) { + super(ctxt, br, parserFeatures, formatFeatures, codec, reader); + } + + public Map getLines() { + return ranges; + } + + public Map> getPaths() { + return paths; + } + + public Set getErrors() { + return errors; + } + + @Override + public JsonToken nextToken() throws IOException { + JsonToken token = null; + try { + token = super.nextToken(); + } catch (Exception e) { + e.printStackTrace(); + // if (strictMode) { + // throw e; + // } + } + + if (token != null) { + processLineEntry(token, getCurrentLocation(), getParsingContext()); + } + + return token; + } + + private void processLineEntry(JsonToken token, JsonLocation location, JsonStreamContext context) { + /* + * Root needs to be handled specially. + */ + if (!seenRoot) { + JsonRegion range = getOrCreateRange(ptr); + range.setContentLocation(new Location( // + location.getLineNr(), // + location.getColumnNr(), // + location.getLineNr(), // + location.getColumnNr())); + + ranges.put(ptr, range); + paths.put(ptr, Sets. newHashSet()); + + seenRoot = true; + return; + } + + /* + * We get that if JSON Pointer "" points to a container... We need to skip that + */ + if (context.inRoot()) { + JsonRegion range = ranges.get(ptr); + Location previousLocation = range.getContentLocation(); + range.setContentLocation(new Location( // + previousLocation.startLine, // + previousLocation.startColumn, // + location.getLineNr(), // + location.getColumnNr())); + + return; + } + + /* + * If the end of a container, "pop" one level + */ + if (token == JsonToken.END_OBJECT || token == JsonToken.END_ARRAY) { + JsonRegion range = getOrCreateRange(ptr); + Location previousLocation = range.getContentLocation(); + range.setContentLocation(new Location( // + previousLocation.startLine, // + previousLocation.startColumn, // + location.getLineNr(), // + location.getColumnNr())); + + ptr = ptr.head(); + + return; + } + + /* + * This is not addressable... + */ + if (token == JsonToken.FIELD_NAME) { + Set list = paths.get(ptr.head()); + if (list == null) { + list = Sets.newHashSet(); + } + list.add(ptr); + + JsonPointer fieldPointer = append(ptr, context); + + // Add error if object already contains field with same name + if (ranges.containsKey(fieldPointer)) { + if (!isInDuplicateParent(fieldPointer)) { + duplicateKeys.add(fieldPointer); + + errors.add(new SwaggerError(location.getLineNr(), IMarker.SEVERITY_WARNING, + String.format(Messages.error_duplicate_keys, context.getCurrentName()))); + } + } + + JsonRegion range = getOrCreateRange(fieldPointer); + range.setFieldLocation(new Location( // + location.getLineNr(), // + location.getColumnNr(), // + location.getLineNr(), // + location.getColumnNr())); + + paths.put(ptr.head(), list); + return; + } + + final JsonStreamContext parent = context.getParent(); + + /* + * But this is; however we need to know what the parent is to do things correctly, delegate to another method + */ + if (token == JsonToken.START_ARRAY || token == JsonToken.START_OBJECT) { + startContainer(parent, location, token == JsonToken.START_ARRAY); + return; + } + + /* + * OK, "normal" entry, build the pointer + */ + final JsonPointer entryPointer = append(ptr, context); + + JsonRegion range = getOrCreateRange(entryPointer); + range.setContentLocation(new Location( // + location.getLineNr(), // + location.getColumnNr(), // + location.getLineNr(), // + location.getColumnNr())); + + JsonPointer top = entryPointer.head(); + Set list = paths.get(top); + if (list == null) { + list = Sets.newHashSet(); + } + list.add(entryPointer); + paths.put(top, list); + } + + protected JsonRegion getOrCreateRange(JsonPointer pointer) { + JsonRegion range = ranges.get(pointer); + if (range == null) { + ranges.put(pointer, range = new JsonRegion(pointer)); + } + return range; + } + + private void startContainer(final JsonStreamContext parent, JsonLocation location, boolean isArray) { + ptr = append(ptr, parent); + + if (isArray) { + Integer count = arrayContent.get(ptr); + if (count == null) { + arrayContent.put(ptr, 0); + } + } + + JsonRegion range = getOrCreateRange(ptr); + range.setContentLocation(new Location( // + location.getLineNr(), // + location.getColumnNr(), // + location.getLineNr(), // + location.getColumnNr())); + + Set list = paths.get(ptr.head()); + if (list == null) { + list = Sets.newHashSet(); + } + list.add(ptr); + paths.put(ptr.head(), list); + } + + private JsonPointer append(JsonPointer ptr, JsonStreamContext context) { + JsonPointer result = ptr; + + if (context.inArray()) { + // context.getCurrentIndex() does not return anything other than 0 + // so we keep track of number of elements in an array with the arrayContent map by + // incrementing it each time we append something to the array pointer. + + int count = arrayContent.get(ptr); + result = ptr.append(JsonPointer.compile("/" + count)); + arrayContent.put(ptr, ++count); + } else if (context.inObject()) { + result = ptr.append(JsonPointer.compile("/" + context.getCurrentName().replaceAll("/", "~1"))); + } + + return result; + } + + public void setStrict(boolean strict) { + this.strictMode = strict; + } + + private boolean isInDuplicateParent(JsonPointer ptr) { + boolean result = false; + JsonPointer parent = ptr; + + while ((parent = parent.head()) != null && result == false) { + if (duplicateKeys.contains(parent)) { + result = true; + } + } + return result; + } + +} diff --git a/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/json/references/JsonReference.java b/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/json/references/JsonReference.java index ed805f08..5b860ceb 100644 --- a/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/json/references/JsonReference.java +++ b/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/json/references/JsonReference.java @@ -12,17 +12,10 @@ import java.net.URI; -import org.yaml.snakeyaml.nodes.NodeId; -import org.yaml.snakeyaml.nodes.NodeTuple; -import org.yaml.snakeyaml.nodes.ScalarNode; - import com.fasterxml.jackson.core.JsonPointer; import com.fasterxml.jackson.databind.JsonNode; import com.google.common.base.Strings; import com.reprezen.swagedit.core.editor.JsonDocument; -import com.reprezen.swagedit.core.model.AbstractNode; -import com.reprezen.swagedit.core.model.ObjectNode; -import com.reprezen.swagedit.core.model.ValueNode; /** * Represents a JSON reference as defined by https://tools.ietf.org/html/draft-pbryan-zyp-json-ref-03. @@ -167,10 +160,6 @@ public static boolean isReference(JsonNode value) { return value != null && value.isObject() && value.has(PROPERTY) && value.get(PROPERTY).isTextual(); } - public static boolean isReference(AbstractNode value) { - return value != null && value.isObject() && value.get(PROPERTY) != null; - } - public static JsonPointer getPointer(JsonNode node) { JsonNode value = node.get(PROPERTY); @@ -192,30 +181,20 @@ private static JsonPointer createPointer(String text) { return JsonPointer.compile(text); } - /** - * Returns true if the argument can be identified as a JSON reference node. - * - * @param tuple - * @return true if a reference node - */ - public static boolean isReference(NodeTuple tuple) { - if (tuple.getKeyNode().getNodeId() == NodeId.scalar) { - String value = ((ScalarNode) tuple.getKeyNode()).getValue(); - - return JsonReference.PROPERTY.equals(value) && tuple.getValueNode().getNodeId() == NodeId.scalar; - } - return false; - } - - public static JsonPointer getPointer(ObjectNode node) { - ValueNode value = node.get(PROPERTY).asValue(); - - if (value != null) { - return createPointer((String) value.getValue()); - } else { - return createPointer(null); - } - } + // /** + // * Returns true if the argument can be identified as a JSON reference node. + // * + // * @param tuple + // * @return true if a reference node + // */ + // public static boolean isReference(NodeTuple tuple) { + // if (tuple.getKeyNode().getNodeId() == NodeId.scalar) { + // String value = ((ScalarNode) tuple.getKeyNode()).getValue(); + // + // return JsonReference.PROPERTY.equals(value) && tuple.getValueNode().getNodeId() == NodeId.scalar; + // } + // return false; + // } /** * Returns the JSON document that contains the node referenced by this reference. diff --git a/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/json/references/JsonReferenceCollector.java b/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/json/references/JsonReferenceCollector.java index 5f61a560..511bd2a3 100644 --- a/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/json/references/JsonReferenceCollector.java +++ b/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/json/references/JsonReferenceCollector.java @@ -10,14 +10,13 @@ *******************************************************************************/ package com.reprezen.swagedit.core.json.references; -import static com.reprezen.swagedit.core.json.references.JsonReference.PROPERTY; - import java.net.URI; import java.util.Map; +import com.fasterxml.jackson.core.JsonPointer; +import com.fasterxml.jackson.databind.JsonNode; import com.google.common.collect.Maps; -import com.reprezen.swagedit.core.model.AbstractNode; -import com.reprezen.swagedit.core.model.Model; +import com.reprezen.swagedit.core.editor.JsonDocument; /** * Collector of JSON references present in a JSON or YAML document. @@ -40,18 +39,18 @@ public JsonReferenceCollector(JsonReferenceFactory factory) { * @param model * @return all reference nodes */ - public Map collect(URI baseURI, Model model) { - final Map references = Maps.newHashMap(); - - for (AbstractNode node : model.allNodes()) { - if (factory.isReference(node)) { - JsonReference reference = factory.createSimpleReference(baseURI, node.get(PROPERTY)); - if (reference == null) { - reference = factory.create(node); - } - if (reference != null) { - references.put(node, reference); - } + public Map collect(URI baseURI, JsonDocument document) { + final Map references = Maps.newHashMap(); + final JsonNode json = document.asJson(); + + for (JsonPointer pointer : document.getContent().getReferences()) { + JsonNode refNode = json.at(pointer); + JsonReference reference = factory.createSimpleReference(baseURI, json, refNode); + if (reference == null) { + reference = factory.create(refNode); + } + if (reference != null) { + references.put(refNode, reference); } } diff --git a/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/json/references/JsonReferenceFactory.java b/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/json/references/JsonReferenceFactory.java index 2e5b3b56..09401261 100644 --- a/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/json/references/JsonReferenceFactory.java +++ b/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/json/references/JsonReferenceFactory.java @@ -19,9 +19,6 @@ import com.fasterxml.jackson.core.JsonPointer; import com.fasterxml.jackson.databind.JsonNode; import com.google.common.base.Strings; -import com.reprezen.swagedit.core.model.AbstractNode; -import com.reprezen.swagedit.core.model.Model; -import com.reprezen.swagedit.core.model.ValueNode; import com.reprezen.swagedit.core.utils.URLUtils; /** @@ -32,18 +29,18 @@ */ public class JsonReferenceFactory { - public JsonReference create(AbstractNode node) { - if (node == null) { - return new JsonReference(null, null, false, false, false, node); - } - - ValueNode value = getReferenceValue(node); - if (value != null) { - return doCreate((String) value.getValue(), value); - } else { - return null; - } - } + // public JsonReference create(AbstractNode node) { + // if (node == null) { + // return new JsonReference(null, null, false, false, false, node); + // } + // + // ValueNode value = getReferenceValue(node); + // if (value != null) { + // return doCreate((String) value.getValue(), value); + // } else { + // return null; + // } + // } public JsonReference create(JsonNode node) { if (node == null || node.isMissingNode()) { @@ -68,28 +65,20 @@ public JsonReference create(ScalarNode node) { * @param value * @return reference */ - public JsonReference createSimpleReference(URI baseURI, AbstractNode valueNode) { + public JsonReference createSimpleReference(URI baseURI, JsonNode document, JsonNode valueNode) { if (valueNode == null || valueNode.isArray() || valueNode.isObject()) { return null; } - final Object value = valueNode.asValue().getValue(); - if (!(value instanceof String)) { + String value = valueNode.asText(); + if (Strings.emptyToNull(value) == null || value.startsWith("#") || value.contains("/")) { return null; } - String stringValue = (String) value; - if (Strings.emptyToNull(stringValue) == null || stringValue.startsWith("#") || stringValue.contains("/")) { - return null; - } - - final Model model = valueNode.getModel(); - if (model != null) { - JsonPointer ptr = JsonPointer.compile("/definitions/" + value); - AbstractNode target = model.find(ptr); - if (target != null) { - return new JsonReference.SimpleReference(baseURI, ptr, valueNode); - } + JsonPointer ptr = JsonPointer.compile("/definitions/" + value); + JsonNode target = document.at(ptr); + if (target != null && !target.isMissingNode()) { + return new JsonReference.SimpleReference(baseURI, ptr, valueNode); } return null; @@ -132,20 +121,20 @@ public JsonReference doCreate(String value, Object source) { return new JsonReference(uri, pointer, absolute, local, warnings, source); } - protected Boolean isReference(AbstractNode node) { + protected Boolean isReference(JsonNode node) { return JsonReference.isReference(node); } - protected ValueNode getReferenceValue(AbstractNode node) { - if (node.isValue()) { - return node.asValue(); - } - AbstractNode value = node.get(PROPERTY); - if (value != null && value.isValue()) { - return value.asValue(); - } - return null; - } + // protected JsonNode getReferenceValue(JsonNode node) { + // if (node.isValue()) { + // return node.asValue(); + // } + // AbstractNode value = node.get(PROPERTY); + // if (value != null && value.isValue()) { + // return value.asValue(); + // } + // return null; + // } protected String getReferenceValue(JsonNode node) { return node.isTextual() ? node.asText() : node.get(PROPERTY).asText(); diff --git a/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/json/references/JsonReferenceValidator.java b/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/json/references/JsonReferenceValidator.java index 5bbfe8cb..119f7634 100644 --- a/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/json/references/JsonReferenceValidator.java +++ b/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/json/references/JsonReferenceValidator.java @@ -11,7 +11,6 @@ package com.reprezen.swagedit.core.json.references; import static com.reprezen.swagedit.core.validation.Messages.error_invalid_reference; -import static com.reprezen.swagedit.core.validation.Messages.error_invalid_reference_type; import static com.reprezen.swagedit.core.validation.Messages.error_missing_reference; import static com.reprezen.swagedit.core.validation.Messages.warning_simple_reference; import static org.eclipse.core.resources.IMarker.SEVERITY_ERROR; @@ -23,14 +22,9 @@ import java.util.Set; import com.fasterxml.jackson.databind.JsonNode; -import com.github.fge.jsonschema.core.exceptions.ProcessingException; -import com.github.fge.jsonschema.core.report.ProcessingReport; -import com.github.fge.jsonschema.main.JsonSchema; import com.github.fge.jsonschema.main.JsonSchemaFactory; import com.google.common.collect.Sets; import com.reprezen.swagedit.core.editor.JsonDocument; -import com.reprezen.swagedit.core.model.AbstractNode; -import com.reprezen.swagedit.core.schema.TypeDefinition; import com.reprezen.swagedit.core.validation.SwaggerError; /** @@ -57,13 +51,14 @@ public void setFactory(JsonSchemaFactory factory) { * @return collection of errors */ public Collection validate(URI baseURI, JsonDocument doc) { - return doValidate(baseURI, doc, collector.collect(baseURI, doc.getModel())); + return doValidate(baseURI, doc, collector.collect(baseURI, doc)); } protected Collection doValidate(URI baseURI, JsonDocument doc, - Map references) { + Map references) { + Set errors = Sets.newHashSet(); - for (AbstractNode node : references.keySet()) { + for (JsonNode node : references.keySet()) { JsonReference reference = references.get(node); if (reference instanceof JsonReference.SimpleReference) { @@ -93,24 +88,25 @@ protected Collection doValidate(URI baseURI, JsonDocumen * @param errors * current set of errors */ - protected void validateType(JsonDocument doc, URI baseURI, AbstractNode node, JsonReference reference, + protected void validateType(JsonDocument doc, URI baseURI, JsonNode node, JsonReference reference, Set errors) { - JsonNode target = findTarget(doc, baseURI, reference); - TypeDefinition type = node.getType(); - - ProcessingReport report; - if (factory != null) { - try { - JsonSchema jsonSchema = factory.getJsonSchema(doc.getSchema().asJson(), type.getPointer().toString()); - report = jsonSchema.validate(target); - if (!report.isSuccess()) { - errors.add(createReferenceError(SEVERITY_WARNING, error_invalid_reference_type, reference)); - } - } catch (ProcessingException e) { - errors.add(createReferenceError(SEVERITY_WARNING, error_invalid_reference_type, reference)); - } - } + JsonNode target = findTarget(doc, baseURI, reference); + // TypeDefinition type = node.getType(); + // + // ProcessingReport report; + // if (factory != null) { + // try { + // JsonSchema jsonSchema = factory.getJsonSchema(doc.getSchema().asJson(), type.getPointer().toString()); + // report = jsonSchema.validate(target); + // + // if (!report.isSuccess()) { + // errors.add(createReferenceError(SEVERITY_WARNING, error_invalid_reference_type, reference)); + // } + // } catch (ProcessingException e) { + // errors.add(createReferenceError(SEVERITY_WARNING, error_invalid_reference_type, reference)); + // } + // } } protected JsonNode findTarget(JsonDocument doc, URI baseURI, JsonReference reference) { @@ -137,11 +133,11 @@ protected JsonNode findTarget(JsonDocument doc, URI baseURI, JsonReference refer protected SwaggerError createReferenceError(int severity, String message, JsonReference reference) { Object source = reference.getSource(); int line; - if (source instanceof AbstractNode) { - line = ((AbstractNode) source).getStart().getLine() + 1; - } else { + // if (source instanceof AbstractNode) { + // line = ((AbstractNode) source).getStart().getLine() + 1; + // } else { line = 1; - } + // } return new SwaggerError(line, severity, message); } diff --git a/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/model/AbstractNode.java b/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/model/AbstractNode.java deleted file mode 100644 index a30babe6..00000000 --- a/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/model/AbstractNode.java +++ /dev/null @@ -1,268 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2016 ModelSolv, Inc. and others. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * ModelSolv, Inc. - initial API and implementation and/or initial documentation - *******************************************************************************/ -package com.reprezen.swagedit.core.model; - -import java.util.Collections; -import java.util.Objects; - -import org.eclipse.jface.text.BadLocationException; -import org.eclipse.jface.text.IDocument; -import org.eclipse.jface.text.Position; - -import com.fasterxml.jackson.core.JsonPointer; -import com.google.common.collect.Iterables; -import com.reprezen.swagedit.core.schema.TypeDefinition; - -/** - * Represents a Node inside a YAML/JSON document. - * - *
- * - * Nodes can be either Values, Objects or Arrays. They can contain other elements or simply a value. They contain also - * information about their location inside a document. A node can be given a type from a JSON schema. - * - */ -public abstract class AbstractNode { - - private final Model model; - private final JsonPointer pointer; - private final AbstractNode parent; - - private String property; - private TypeDefinition type; - private Location start; - private Location end; - - AbstractNode(Model model, AbstractNode parent, JsonPointer ptr) { - this.model = model; - this.parent = parent; - this.pointer = ptr; - } - - public Model getModel() { - return model; - } - - /** - * Returns the child node that is contained at the given index. - * - * @param pos - * @return node - */ - public AbstractNode get(int index) { - return null; - } - - /** - * Returns the child node that is contained by the given property. - * - * @param property - * @return node - */ - public AbstractNode get(String property) { - return null; - } - - /** - * Returns true if the node is an object. - * - * @return true if object - */ - public boolean isObject() { - return false; - } - - /** - * Returns true if the node is an array. - * - * @return true if array - */ - public boolean isArray() { - return false; - } - - /** - * Returns true if the node is a value - * - * @return true if value - */ - public boolean isValue() { - return false; - } - - /** - * Returns the current node as an {@link ObjectNode} if it is an instance, or null otherwise. - * - * @return node - */ - public ObjectNode asObject() { - return null; - } - - /** - * Returns the current node as an {@link ArrayNode} if it is an instance, or null otherwise. - * - * @return node - */ - public ArrayNode asArray() { - return null; - } - - /** - * Returns the current node as an {@link ValueNode} if it is an instance, or null otherwise. - * - * @return node - */ - public ValueNode asValue() { - return null; - } - - /** - * Returns the JSON pointer that identifies this node. - * - * @return JSON pointer - */ - public JsonPointer getPointer() { - return pointer; - } - - /** - * - * @return JSON pointer as a string - */ - public String getPointerString() { - return getPointer().toString(); - } - - public void setType(TypeDefinition type) { - this.type = type; - } - - public TypeDefinition getType() { - return type; - } - - /** - * Returns the parent node that contains this node, or null if the node is the root node. - * - * @return parent node - */ - public AbstractNode getParent() { - return parent; - } - - public String getProperty() { - return property; - } - - public void setProperty(String name) { - this.property = name; - } - - /** - * Returns the children elements of this node. - * - * @return node's children - */ - public Iterable elements() { - return Collections.emptyList(); - } - - /** - * Returns the number of elements contained by this node. - * - * @return size of children - */ - public int size() { - return Iterables.size(elements()); - } - - public abstract String getText(); - - /** - * Returns the position of the node inside the given document.
- * The position matches the area that contains all the node's content. - * - * @param document - * @return position inside the document - */ - public Position getPosition(IDocument document) { - boolean selectEntireElement = false; - int startLine = getStart().getLine(); - int offset = 0; - int length = 0; - - int endLine = getEnd().getLine(); - int endCol = getEnd().getColumn(); - try { - offset = document.getLineOffset(startLine); - if (selectEntireElement) { - length = (document.getLineOffset(endLine) + endCol) - offset; - } else if (startLine < document.getNumberOfLines() - 1) { - length = document.getLineOffset(startLine+1) - offset; - } else { - length = document.getLineLength(startLine); - } - } catch (BadLocationException e) { - return new Position(0); - } - - return new Position(Math.max(0, offset), length); - } - - public void setStartLocation(Location start) { - this.start = start; - } - - public void setEndLocation(Location location) { - this.end = location; - } - - /** - * Returns the start location of this node. - * - * @return start location - */ - public Location getStart() { - return start; - } - - /** - * Returns the end location of this node. - * - * @return end location - */ - public Location getEnd() { - return end; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((parent == null) ? 0 : parent.hashCode()); - result = prime * result + ((pointer == null) ? 0 : pointer.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - AbstractNode other = (AbstractNode) obj; - return Objects.equals(getPointer(), other.getPointer()); - } - -} diff --git a/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/model/ArrayNode.java b/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/model/ArrayNode.java deleted file mode 100644 index 7b759f37..00000000 --- a/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/model/ArrayNode.java +++ /dev/null @@ -1,59 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2016 ModelSolv, Inc. and others. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * ModelSolv, Inc. - initial API and implementation and/or initial documentation - *******************************************************************************/ -package com.reprezen.swagedit.core.model; - -import java.util.ArrayList; -import java.util.List; - -import com.fasterxml.jackson.core.JsonPointer; - -public class ArrayNode extends AbstractNode { - - private final List elements = new ArrayList<>(); - - ArrayNode(Model model, AbstractNode parent, JsonPointer ptr) { - super(model, parent, ptr); - } - - @Override - public AbstractNode get(int pos) { - return elements.get(pos); - } - - @Override - public ArrayNode asArray() { - return this; - } - - @Override - public boolean isArray() { - return true; - } - - public void add(AbstractNode model) { - this.elements.add(model); - } - - @Override - public Iterable elements() { - return elements; - } - - @Override - public String getText() { - return getProperty() == null ? "" : getProperty(); - } - - @Override - public String toString() { - return "[ " + elements + " ]"; - } -} \ No newline at end of file diff --git a/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/model/Location.java b/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/model/Location.java deleted file mode 100644 index dda3f645..00000000 --- a/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/model/Location.java +++ /dev/null @@ -1,45 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2016 ModelSolv, Inc. and others. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * ModelSolv, Inc. - initial API and implementation and/or initial documentation - *******************************************************************************/ -package com.reprezen.swagedit.core.model; - -/** - * Location inside a document represented by a line and a column. - */ -public class Location { - - private final int line; - private final int column; - - public Location(int line, int column) { - this.line = line; - this.column = column; - } - - public int getLine() { - return line; - } - - public int getColumn() { - return column; - } - - @Override - public String toString() { - StringBuilder builder = new StringBuilder(); - builder.append("Location [line="); - builder.append(line); - builder.append(", column="); - builder.append(column); - builder.append("]"); - return builder.toString(); - } - -} diff --git a/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/model/Model.java b/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/model/Model.java deleted file mode 100644 index bd862905..00000000 --- a/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/model/Model.java +++ /dev/null @@ -1,413 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2016 ModelSolv, Inc. and others. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * ModelSolv, Inc. - initial API and implementation and/or initial documentation - *******************************************************************************/ -package com.reprezen.swagedit.core.model; - -import static com.reprezen.swagedit.core.model.NodeDeserializer.ATTRIBUTE_MODEL; -import static com.reprezen.swagedit.core.model.NodeDeserializer.ATTRIBUTE_PARENT; -import static com.reprezen.swagedit.core.model.NodeDeserializer.ATTRIBUTE_POINTER; - -import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.net.URLDecoder; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - -import org.eclipse.core.resources.IFile; -import org.eclipse.core.runtime.IPath; - -import com.fasterxml.jackson.core.JsonPointer; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.ObjectReader; -import com.fasterxml.jackson.databind.module.SimpleModule; -import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; -import com.google.common.base.Strings; -import com.google.common.collect.Iterables; -import com.google.common.collect.Lists; -import com.reprezen.swagedit.core.schema.CompositeSchema; - -/** - * Represents the content of a YAML/JSON document. - * - */ -public class Model { - - private final Map nodes = new LinkedHashMap<>(); - private final CompositeSchema schema; - private IPath path; - - private Model(CompositeSchema schema) { - this(schema, null); - } - - private Model(CompositeSchema schema, IPath path) { - this.schema = schema; - this.path = path; - } - - /** - * Returns an empty model - * - * @param schema - * @return empty model - */ - public static Model empty(CompositeSchema schema) { - Model model = new Model(schema); - ObjectNode root = new ObjectNode(model, null, JsonPointer.compile("")); - root.setType(model.schema.getType(root)); - model.add(root); - - return model; - } - - /** - * Returns a model build by parsing a YAML content. - * - * @param text - * @return model - */ - public static Model parseYaml(CompositeSchema schema, String text) { - if (Strings.emptyToNull(text) == null) { - return empty(schema); - } - - Model model = new Model(schema); - try { - reader(model).readValue(text); - } catch (IllegalArgumentException | IOException e) { - return null; - } - - for (AbstractNode node : model.allNodes()) { - node.setType(model.schema.getType(node)); - } - - return model; - } - - public static Model parse(CompositeSchema schema, JsonNode document) { - ObjectMapper mapper = new ObjectMapper(new YAMLFactory()); - String text = null; - try { - text = mapper.writeValueAsString(document); - } catch (JsonProcessingException e) { - e.printStackTrace(); - } - - return parseYaml(schema, text); - } - - /** - * Parses all files into a list of models. - * - * @param files - * @return list of models - */ - public static Iterable parseYaml(Iterable files, final CompositeSchema schema) { - if (files == null || Iterables.isEmpty(files)) { - return Lists.newArrayList(); - } - - final List models = Lists.newArrayList(); - for (IFile file : files) { - Model model = new Model(schema, file.getFullPath()); - try { - reader(model).readValue(file.getLocationURI().toURL()); - } catch (IllegalArgumentException | IOException e) { - e.printStackTrace(); - continue; - } - - models.add(model); - } - return models; - } - - protected static ObjectMapper createMapper() { - final ObjectMapper mapper = new ObjectMapper(new YAMLFactory()); - final SimpleModule module = new SimpleModule(); - module.addDeserializer(AbstractNode.class, new NodeDeserializer()); - mapper.registerModule(module); - return mapper; - } - - protected static ObjectReader reader(Model model) { - return createMapper().reader() // - .withAttribute(ATTRIBUTE_MODEL, model) // - .withAttribute(ATTRIBUTE_PARENT, null) // - .withAttribute(ATTRIBUTE_POINTER, JsonPointer.compile("")) // - .forType(AbstractNode.class); - } - - /** - * Creates a new object node - * - * @param node - * parent or null - * @param node - * pointer - * @return object node - */ - public ObjectNode objectNode(AbstractNode parent, JsonPointer ptr) { - return (ObjectNode) add(new ObjectNode(this, parent, ptr)); - } - - /** - * Creates a new array node - * - * @param node - * parent or null - * @param node - * pointer - * @return array node - */ - public ArrayNode arrayNode(AbstractNode parent, JsonPointer ptr) { - return (ArrayNode) add(new ArrayNode(this, parent, ptr)); - } - - /** - * Creates a new value node - * - * @param node - * parent or null - * @param node - * pointer - * @param node - * value - * @return value node - */ - public ValueNode valueNode(AbstractNode parent, JsonPointer ptr, Object value) { - return (ValueNode) add(new ValueNode(this, parent, ptr, value)); - } - - /** - * Returns the path of the file that contains the model content. - * - * @param path - */ - public IPath getPath() { - return path; - } - - public void setPath(IPath path) { - this.path = path; - } - - /** - * Returns the node inside the model that can be - * - * @param pointer - * @return node - */ - public AbstractNode find(JsonPointer pointer) { - return nodes.get(pointer); - } - - public AbstractNode find(String pointer) { - if (Strings.emptyToNull(pointer) == null) { - return null; - } - if (pointer.startsWith("#")) { - pointer = pointer.substring(1); - } - if (pointer.length() > 1 && pointer.endsWith("/")) { - pointer = pointer.substring(0, pointer.length() - 1); - } - - try { - pointer = URLDecoder.decode(pointer, "UTF-8"); - } catch (UnsupportedEncodingException e) { - // leave the pointer as it is - } - - try { - return nodes.get(JsonPointer.valueOf(pointer)); - } catch (Exception e) { - return null; - } - } - - private AbstractNode add(AbstractNode node) { - if (node != null && node.getPointer() != null) { - nodes.put(node.getPointer(), node); - } - return node; - } - - /** - * Returns the model's root node. - * - * @return node - */ - public AbstractNode getRoot() { - return nodes.get(JsonPointer.compile("")); - } - - /** - * Returns the pointer for the node whose content is at the position specified by a line and column. - * - * @param line - * @param column - * @return - */ - public JsonPointer getPath(int line, int column) { - AbstractNode node = getNode(line, column); - if (node != null) { - return node.getPointer(); - } - return JsonPointer.compile(""); - } - - /** - * Returns the node whose content is at the position specified by a line and column. - * - * @param line - * @param column - * @return - */ - public AbstractNode getNode(int line, int column) { - if (column == 0) { - return getRoot(); - } - - AbstractNode found = forLine(line); - if (found != null) { - found = findChildren(found, line, column); - int c = found.getStart().getColumn(); - if (column > c || (column == c && found.getParent().isArray())) { - return found; - } else { - return found.getParent(); - } - } else { - found = findBeforeLine(line, column); - if (found != null) { - return findCorrectNode(found, column); - } - } - - return found; - } - - /** - * Returns all nodes within this model - * - * @return iterable of nodes - */ - public Iterable allNodes() { - return nodes.values(); - } - - protected AbstractNode findChildren(AbstractNode current, int line, int column) { - for (AbstractNode el : current.elements()) { - if (el.getStart().getLine() == line) { - if (el instanceof ValueNode) { - if (column >= contentColumn(el)) { - return el; - } - } else { - if (column >= el.getStart().getColumn()) { - return el; - } - } - } - } - return current; - } - - protected AbstractNode findCorrectNode(AbstractNode current, int column) { - if (current.getStart().getColumn() == column) { - if (current.getParent() instanceof ObjectNode) { - return current.getParent(); - } - } - - if (current.getStart().getColumn() < column) { - return current; - } else { - return findCorrectNode(current.getParent(), column); - } - } - - protected AbstractNode forLine(int line) { - final AbstractNode root = getRoot(); - for (AbstractNode node : allNodes()) { - if (node != root && node.getStart().getLine() == line) { - return node; - } - } - return null; - } - - protected AbstractNode findBeforeLine(int line, int column) { - AbstractNode root = getRoot(); - AbstractNode found = null, before = null; - Iterator it = allNodes().iterator(); - - while (found == null && it.hasNext()) { - AbstractNode current = it.next(); - if (root == current) { - continue; - } - - if (current.getStart().getLine() < line) { - before = current; - } else { - found = before; - } - } - - if (found == null && before != null) { - found = before; - } - - return found; - } - - protected int contentColumn(AbstractNode n) { - String property = Strings.emptyToNull(n.getProperty()); - if (property == null) { - return n.getStart().getColumn(); - } - - return (property.length() + 1) + n.getStart().getColumn(); - } - - /** - * Returns all the nodes whose type match the given pointer. - * - * @param typePointer - * pointer of a type present in the schema - * @return list of nodes being instance of the type - */ - public List findByType(JsonPointer typePointer) { - List instances = Lists.newArrayList(); - for (AbstractNode node : allNodes()) { - if (node.getType() != null && typePointer.equals(node.getType().getPointer())) { - instances.add(node); - } - } - return instances; - } - - /** - * Returns the schema associated to this model. - * - * @return schema - */ - public CompositeSchema getSchema() { - return schema; - } - -} diff --git a/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/model/NodeDeserializer.java b/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/model/NodeDeserializer.java deleted file mode 100644 index f7e9ee21..00000000 --- a/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/model/NodeDeserializer.java +++ /dev/null @@ -1,118 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2016 ModelSolv, Inc. and others. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * ModelSolv, Inc. - initial API and implementation and/or initial documentation - *******************************************************************************/ -package com.reprezen.swagedit.core.model; - -import java.io.IOException; - -import com.fasterxml.jackson.core.JsonLocation; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonPointer; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.core.JsonToken; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; - -public class NodeDeserializer extends JsonDeserializer { - - public static final String ATTRIBUTE_MODEL = "model"; - public static final String ATTRIBUTE_PARENT = "parent"; - public static final String ATTRIBUTE_POINTER = "pointer"; - - @Override - public AbstractNode deserialize(JsonParser p, DeserializationContext context) - throws IOException, JsonProcessingException { - - JsonLocation startLocation = p.getTokenLocation(); - if (p.getCurrentToken() == JsonToken.FIELD_NAME) { - p.nextToken(); - } - - switch (p.getCurrentToken()) { - case START_OBJECT: - return deserializeObjectNode(p, context, startLocation); - case START_ARRAY: - return deserializeArrayNode(p, context, startLocation); - default: - return deserializeValueNode(p, context, startLocation); - } - } - - protected ObjectNode deserializeObjectNode(JsonParser p, DeserializationContext context, JsonLocation startLocation) - throws IllegalArgumentException, IOException { - - final Model model = (Model) context.getAttribute(ATTRIBUTE_MODEL); - final AbstractNode parent = (AbstractNode) context.getAttribute(ATTRIBUTE_PARENT); - final JsonPointer ptr = (JsonPointer) context.getAttribute(ATTRIBUTE_POINTER); - - final ObjectNode node = model.objectNode(parent, ptr); - node.setStartLocation(createLocation(startLocation)); - - while (p.nextToken() != JsonToken.END_OBJECT) { - String name = p.getCurrentName(); - - JsonPointer pp = JsonPointer.compile(ptr.toString() + "/" + name.replaceAll("/", "~1")); - context.setAttribute(ATTRIBUTE_PARENT, node); - context.setAttribute(ATTRIBUTE_POINTER, pp); - - AbstractNode v = deserialize(p, context); - v.setProperty(name); - node.put(name, v); - } - - node.setEndLocation(createLocation(p.getCurrentLocation())); - return node; - } - - protected ArrayNode deserializeArrayNode(JsonParser p, DeserializationContext context, JsonLocation startLocation) - throws IOException { - final Model model = (Model) context.getAttribute(ATTRIBUTE_MODEL); - final AbstractNode parent = (AbstractNode) context.getAttribute(ATTRIBUTE_PARENT); - final JsonPointer ptr = (JsonPointer) context.getAttribute(ATTRIBUTE_POINTER); - - ArrayNode node = model.arrayNode(parent, ptr); - - int i = 0; - while (p.nextToken() != JsonToken.END_ARRAY) { - JsonPointer pp = JsonPointer.compile(ptr.toString() + "/" + i); - - context.setAttribute(ATTRIBUTE_PARENT, node); - context.setAttribute(ATTRIBUTE_POINTER, pp); - - AbstractNode v = deserialize(p, context); - - node.add(v); - i++; - } - - node.setStartLocation(createLocation(startLocation)); - node.setEndLocation(createLocation(p.getCurrentLocation())); - return node; - } - - protected ValueNode deserializeValueNode(JsonParser p, DeserializationContext context, JsonLocation startLocation) - throws IOException { - final Model model = (Model) context.getAttribute(ATTRIBUTE_MODEL); - final AbstractNode parent = (AbstractNode) context.getAttribute(ATTRIBUTE_PARENT); - final JsonPointer ptr = (JsonPointer) context.getAttribute(ATTRIBUTE_POINTER); - - Object v = context.readValue(p, Object.class); - - ValueNode node = model.valueNode(parent, ptr, v); - node.setStartLocation(createLocation(startLocation)); - node.setEndLocation(createLocation(p.getCurrentLocation())); - - return node; - } - - private Location createLocation(JsonLocation json) { - return new Location(json.getLineNr() - 1, json.getColumnNr() - 1); - } -} diff --git a/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/model/ObjectNode.java b/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/model/ObjectNode.java deleted file mode 100644 index c7ce6b24..00000000 --- a/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/model/ObjectNode.java +++ /dev/null @@ -1,71 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2016 ModelSolv, Inc. and others. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * ModelSolv, Inc. - initial API and implementation and/or initial documentation - *******************************************************************************/ -package com.reprezen.swagedit.core.model; - -import java.util.Collection; -import java.util.LinkedHashMap; -import java.util.Map; - -import com.fasterxml.jackson.core.JsonPointer; -import com.google.common.collect.Iterables; - -public class ObjectNode extends AbstractNode { - - private final Map elements = new LinkedHashMap<>(); - - ObjectNode(Model model, AbstractNode parent, JsonPointer ptr) { - super(model, parent, ptr); - } - - @Override - public AbstractNode get(int pos) { - return Iterables.get(elements.values(), pos); - } - - @Override - public AbstractNode get(String property) { - return elements.get(property); - } - - public AbstractNode put(String property, AbstractNode value) { - this.elements.put(property, value); - return this; - } - - public Collection fieldNames() { - return elements.keySet(); - } - - @Override - public boolean isObject() { - return true; - } - - @Override - public ObjectNode asObject() { - return this; - } - - @Override - public Iterable elements() { - return elements.values(); - } - - @Override - public String getText() { - return getProperty() == null ? "" : getProperty(); - } - - @Override - public String toString() { - return elements.toString(); - } -} \ No newline at end of file diff --git a/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/model/ValueNode.java b/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/model/ValueNode.java deleted file mode 100644 index 1edf7a05..00000000 --- a/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/model/ValueNode.java +++ /dev/null @@ -1,49 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2016 ModelSolv, Inc. and others. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * ModelSolv, Inc. - initial API and implementation and/or initial documentation - *******************************************************************************/ -package com.reprezen.swagedit.core.model; - -import com.fasterxml.jackson.core.JsonPointer; - -public class ValueNode extends AbstractNode { - - private final Object value; - - ValueNode(Model model, AbstractNode parent, JsonPointer ptr, Object value) { - super(model, parent, ptr); - - this.value = value; - } - - @Override - public boolean isValue() { - return true; - } - - @Override - public ValueNode asValue() { - return this; - } - - public Object getValue() { - return value; - } - - @Override - public String getText() { - String text = getProperty() != null ? getProperty() + ": " : ""; - return text + (value != null ? getValue().toString() : ""); - } - - @Override - public String toString() { - return getText(); - } -} \ No newline at end of file diff --git a/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/schema/ComplexTypeDefinition.java b/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/schema/ComplexTypeDefinition.java index aff284ce..9e8cf278 100644 --- a/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/schema/ComplexTypeDefinition.java +++ b/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/schema/ComplexTypeDefinition.java @@ -12,7 +12,6 @@ import java.util.ArrayList; import java.util.Collection; -import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; @@ -20,7 +19,6 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ArrayNode; import com.google.common.collect.Iterables; -import com.reprezen.swagedit.core.model.AbstractNode; /** * Represents a JSON schema type definition of a complex type, eg oneOf, allOf, anyOf types. @@ -107,39 +105,39 @@ private void collectProperties(String property, TypeDefinition current, List it = getComplexTypes().iterator(); - while (it.hasNext() && !isValid) { - TypeDefinition current = it.next(); - - if (current.getPointer() != null && valueType != null - && current.getPointer().equals(valueType.getPointer())) { - isValid = true; - } - } - - return isValid; - } + // @Override + // public boolean validate(JsonNode valueNode) { + // if (valueNode == null) { + // return false; + // } + // + // TypeDefinition valueType = valueNode.getType(); + // if (valueType == null) { + // return false; + // } + // + // if (valueType instanceof ReferenceTypeDefinition) { + // valueType = ((ReferenceTypeDefinition) valueType).resolve(); + // } + // + // boolean isValid = this == valueType; + // + // if (isValid) { + // return true; + // } + // + // Iterator it = getComplexTypes().iterator(); + // while (it.hasNext() && !isValid) { + // TypeDefinition current = it.next(); + // + // if (current.getPointer() != null && valueType != null + // && current.getPointer().equals(valueType.getPointer())) { + // isValid = true; + // } + // } + // + // return isValid; + // } @Override public String getLabel() { diff --git a/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/schema/CompositeSchema.java b/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/schema/CompositeSchema.java index 069f260d..7cae2659 100644 --- a/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/schema/CompositeSchema.java +++ b/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/schema/CompositeSchema.java @@ -17,7 +17,6 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.base.Strings; -import com.reprezen.swagedit.core.model.AbstractNode; /** * Represents the Swagger Schema. @@ -39,19 +38,21 @@ public JsonNode asJson() { return swaggerType.getType().asJson(); } - /** - * Returns the type of a node. - * - *
- * - * Note: this method should be used only during initialization of a model. - * - * @param node - * @return node's type - */ - public TypeDefinition getType(AbstractNode node) { - JsonPointer pointer = node.getPointer(); - + // /** + // * Returns the type of a node. + // * + // *
+ // * + // * Note: this method should be used only during initialization of a model. + // * + // * @param node + // * @return node's type + // */ + // public TypeDefinition getType(AbstractNode node) { + // return getType(node.getPointer()); + // } + + public TypeDefinition getType(JsonPointer pointer) { if (JsonPointer.compile("").equals(pointer)) { return swaggerType.getType(); } diff --git a/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/schema/ReferenceTypeDefinition.java b/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/schema/ReferenceTypeDefinition.java index e676289e..0784da53 100644 --- a/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/schema/ReferenceTypeDefinition.java +++ b/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/schema/ReferenceTypeDefinition.java @@ -13,7 +13,6 @@ import com.fasterxml.jackson.core.JsonPointer; import com.fasterxml.jackson.databind.JsonNode; import com.reprezen.swagedit.core.json.references.JsonReference; -import com.reprezen.swagedit.core.model.AbstractNode; /** * Represents a JSON reference that should be resolved as a type definition. @@ -64,10 +63,10 @@ public TypeDefinition getPropertyType(String property) { return resolve().getPropertyType(property); } - @Override - public boolean validate(AbstractNode valueNode) { - return resolve().validate(valueNode); - } + // @Override + // public boolean validate(JsonNode valueNode) { + // return resolve().validate(valueNode); + // } @Override public String getLabel() { diff --git a/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/schema/TypeDefinition.java b/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/schema/TypeDefinition.java index 1703cbba..8f41f73f 100644 --- a/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/schema/TypeDefinition.java +++ b/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/schema/TypeDefinition.java @@ -12,7 +12,6 @@ import com.fasterxml.jackson.core.JsonPointer; import com.fasterxml.jackson.databind.JsonNode; -import com.reprezen.swagedit.core.model.AbstractNode; /** * Represents a type defined inside a JSON Schema. @@ -99,21 +98,21 @@ protected static String getProperty(JsonPointer pointer) { return s.substring(s.lastIndexOf("/") + 1).replaceAll("~1", "/"); } - public boolean validate(AbstractNode valueNode) { - if (valueNode == null) { - return false; - } - - TypeDefinition valueType = valueNode.getType(); - if (valueType == null) { - return false; - } - - if (valueType instanceof ReferenceTypeDefinition) { - valueType = ((ReferenceTypeDefinition) valueType).resolve(); - } - - return this == valueType; - } + // public boolean validate(JsonNode valueNode) { + // if (valueNode == null) { + // return false; + // } + // + // TypeDefinition valueType = valueNode.getType(); + // if (valueType == null) { + // return false; + // } + // + // if (valueType instanceof ReferenceTypeDefinition) { + // valueType = ((ReferenceTypeDefinition) valueType).resolve(); + // } + // + // return this == valueType; + // } } diff --git a/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/templates/SchemaBasedTemplateContextType.java b/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/templates/SchemaBasedTemplateContextType.java index 89222102..d5cff94b 100644 --- a/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/templates/SchemaBasedTemplateContextType.java +++ b/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/templates/SchemaBasedTemplateContextType.java @@ -17,7 +17,8 @@ import org.eclipse.jface.text.templates.GlobalTemplateVariables; import org.eclipse.jface.text.templates.TemplateContextType; -import com.reprezen.swagedit.core.model.Model; +import com.fasterxml.jackson.core.JsonPointer; +import com.reprezen.swagedit.core.editor.JsonDocument; import com.reprezen.swagedit.core.schema.ComplexTypeDefinition; import com.reprezen.swagedit.core.schema.ReferenceTypeDefinition; import com.reprezen.swagedit.core.schema.TypeDefinition; @@ -32,11 +33,12 @@ public SchemaBasedTemplateContextType(String id, String name, String... pathToSc addGlobalResolvers(); } - public boolean matches(Model model, final String path) { - if (model == null || model.find(path) == null) { + public boolean matches(JsonDocument doc, final String path) { + if (doc == null || doc.asJson().at(path) == null) { return false; } - TypeDefinition type = model.find(path).getType(); + + TypeDefinition type = doc.getContent().getTypes().get(JsonPointer.compile(path)); return matches(type); } diff --git a/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/validation/ErrorProcessor.java b/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/validation/ErrorProcessor.java index dc9b349f..0de90817 100644 --- a/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/validation/ErrorProcessor.java +++ b/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/validation/ErrorProcessor.java @@ -18,24 +18,24 @@ import java.util.Set; import org.eclipse.core.resources.IMarker; -import org.yaml.snakeyaml.nodes.Node; import com.fasterxml.jackson.databind.JsonNode; import com.github.fge.jsonschema.core.report.ProcessingMessage; import com.github.fge.jsonschema.core.report.ProcessingReport; import com.google.common.base.Joiner; +import com.reprezen.swagedit.core.json.JsonModel; /** * Creates {@link SwaggerError} by processing validation reports generated by the json schema validator. */ public class ErrorProcessor { - private final Node document; - private final JsonNode jsonSchema; + private final JsonNode schema; + private final JsonModel model; - public ErrorProcessor(Node document, JsonNode jsonSchema) { - this.document = document; - this.jsonSchema = jsonSchema; + public ErrorProcessor(JsonModel model, JsonNode jsonSchema) { + this.model = model; + this.schema = jsonSchema; } /** @@ -103,7 +103,7 @@ private boolean isMultiple(JsonNode error) { } private SwaggerError createUnique(JsonNode error, int indent) { - final SwaggerError schemaError = new SwaggerError(getLine(error, document), getLevel(error), indent, + final SwaggerError schemaError = new SwaggerError(getLine(error, model), getLevel(error), indent, rewriteError(error)); return schemaError; @@ -111,8 +111,7 @@ private SwaggerError createUnique(JsonNode error, int indent) { private SwaggerError createMultiple(JsonNode error, int indent) { final MultipleSwaggerErrorBuilder schemaErrorBuilder = new MultipleSwaggerErrorBuilder() - .locatedOn(getLine(error, document)).withSeverity(getLevel(error)).indented(indent) - .basedOnSchema(jsonSchema); + .locatedOn(getLine(error, model)).withSeverity(getLevel(error)).indented(indent).basedOnSchema(schema); final JsonNode reports = error.get("reports"); for (Iterator> it = reports.fields(); it.hasNext();) { diff --git a/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/validation/ModelValidator.java b/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/validation/ModelValidator.java new file mode 100644 index 00000000..0db671c1 --- /dev/null +++ b/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/validation/ModelValidator.java @@ -0,0 +1,176 @@ +package com.reprezen.swagedit.core.validation; + +import java.util.HashSet; +import java.util.Iterator; +import java.util.Objects; +import java.util.Set; + +import org.eclipse.core.resources.IMarker; + +import com.fasterxml.jackson.core.JsonPointer; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.reprezen.swagedit.core.json.JsonModel; +import com.reprezen.swagedit.core.json.JsonRegion; +import com.reprezen.swagedit.core.json.references.JsonReference; +import com.reprezen.swagedit.core.schema.TypeDefinition; + +public class ModelValidator { + + private final JsonNode schemaRefTemplate = new ObjectMapper().createObjectNode() // + .put("$ref", "#/definitions/schema"); + protected final JsonModel model; + + public ModelValidator(JsonModel model) { + this.model = model; + } + + /** + * Validates the model against with different rules that cannot be verified only by JSON schema validation. + * + * @return errors + */ + public Set validateModel() { + final Set errors = new HashSet<>(); + + if (model != null) { + for (JsonPointer ptr : model.getTypes().keySet()) { + JsonNode node = model.getContent().at(ptr); + JsonRegion region = model.getRegion(ptr); + + executeModelValidation(node, region, errors); + } + } + return errors; + } + + protected void executeModelValidation(JsonNode node, JsonRegion region, Set errors) { + checkArrayTypeDefinition(node, region, errors); + checkObjectTypeDefinition(node, region, errors); + } + + /** + * Validates an object type definition. + * + * @param node + * @param region + * @param errors + */ + private void checkObjectTypeDefinition(JsonNode node, JsonRegion region, Set errors) { + if (node != null && node.isObject()) { + if (ValidationUtil.isInDefinition(region.pointer.toString())) { + checkMissingType(node, region, errors); + checkMissingRequiredProperties(node, region, errors); + } + } + } + + /** + * This method checks that the node if an array type definitions includes an items field. + * + * @param node + * @param region + * @param errors + */ + void checkArrayTypeDefinition(JsonNode node, JsonRegion region, Set errors) { + if (hasArrayType(node)) { + JsonNode items = node.get("items"); + if (items == null) { + errors.add(error(region, IMarker.SEVERITY_ERROR, Messages.error_array_missing_items)); + } else { + if (!items.isObject()) { + errors.add(error(getRegion(region, "items"), + IMarker.SEVERITY_ERROR, Messages.error_array_items_should_be_object)); + } + } + } + } + + /** + * Returns true if the node is an array type definition + * + * @param node + * @return true if array definition + */ + protected boolean hasArrayType(JsonNode node) { + if (node.isObject() && node.get("type") != null) { + String typeValue = node.get("type").asText(); + return "array".equalsIgnoreCase(typeValue); + } + return false; + } + + /** + * This method checks that the node if an object definition includes a type field. + * + * @param errors + * @param node + */ + protected void checkMissingType(JsonNode node, JsonRegion region, Set errors) { + // object + if (node.get("properties") != null) { + // bypass this node, it is a property whose name is `properties` + + // if ("properties".equals(node.getProperty())) { + // return; + // } + + if (node.get("type") == null) { + errors.add(error(region, IMarker.SEVERITY_WARNING, Messages.error_object_type_missing)); + } else { + JsonNode typeValue = node.get("type"); + if (!(typeValue.isValueNode()) || !Objects.equals("object", typeValue.asText())) { + errors.add(error(region, IMarker.SEVERITY_ERROR, Messages.error_wrong_type)); + } + } + } else if (isSchemaDefinition(node, null) && node.get("type") == null) { + errors.add(error(region, IMarker.SEVERITY_WARNING, Messages.error_type_missing)); + } + } + + private boolean isSchemaDefinition(JsonNode node, TypeDefinition type) { + // need to use getContent() because asJson() returns resolvedValue is some subclasses + return type != null && schemaRefTemplate.equals(type.getContent()) // + && node.get(JsonReference.PROPERTY) == null // + && node.get("allOf") == null; + } + + /** + * This method checks that the required values for the object type definition contains only valid properties. + * + * @param errors + * @param node + */ + protected void checkMissingRequiredProperties(JsonNode node, JsonRegion region, Set errors) { + if (node.get("required") != null && node.get("required").isArray()) { + com.fasterxml.jackson.databind.node.ArrayNode required = (com.fasterxml.jackson.databind.node.ArrayNode) node + .get("required"); + + JsonNode properties = node.get("properties"); + if (properties == null) { + errors.add(error(region, IMarker.SEVERITY_ERROR, Messages.error_missing_properties)); + } else { + for (Iterator it = required.elements(); it.hasNext();) { + JsonNode prop = it.next(); + if (prop.isValueNode()) { + String value = prop.asText(); + + if (properties.get(value) == null) { + errors.add(error(getRegion(region, value), + IMarker.SEVERITY_ERROR, + String.format(Messages.error_required_properties, value))); + } + } + } + } + } + } + + protected SwaggerError error(JsonRegion region, int level, String message) { + return new SwaggerError(region.getContentLocation().startLine, level, message); + } + + protected JsonRegion getRegion(JsonRegion parent, String ptr) { + return model.getRegion(parent.pointer.append(JsonPointer.compile(ptr))); + } +} diff --git a/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/validation/MultipleSwaggerErrorBuilder.java b/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/validation/MultipleSwaggerErrorBuilder.java index f8b77741..795f82e9 100644 --- a/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/validation/MultipleSwaggerErrorBuilder.java +++ b/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/validation/MultipleSwaggerErrorBuilder.java @@ -93,7 +93,7 @@ public int compare(String o1, String o2) { } protected String getHumanFriendlyText(String location) { - JsonNode swaggerSchemaNode = ValidationUtil.findNode(location, jsonSchema); + JsonNode swaggerSchemaNode = jsonSchema.at(location); if (swaggerSchemaNode == null) { return location; } diff --git a/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/validation/SwaggerError.java b/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/validation/SwaggerError.java index c3b3ccc2..63aa4ee2 100644 --- a/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/validation/SwaggerError.java +++ b/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/validation/SwaggerError.java @@ -14,24 +14,14 @@ import java.util.Set; import org.eclipse.core.resources.IMarker; -import org.yaml.snakeyaml.error.MarkedYAMLException; -import org.yaml.snakeyaml.error.YAMLException; import com.fasterxml.jackson.core.JsonProcessingException; import com.google.common.base.Strings; public class SwaggerError { - - private static YamlErrorProcessor processor = new YamlErrorProcessor(); - - public static SwaggerError newYamlError(YAMLException exception) { - int line = (exception instanceof MarkedYAMLException) - ? ((MarkedYAMLException) exception).getProblemMark().getLine() + 1 : 1; - return new SwaggerError(line, IMarker.SEVERITY_ERROR, 0, processor.rewriteMessage(exception)); - } public static SwaggerError newJsonError(JsonProcessingException exception) { - int line = (exception.getLocation() != null) ? exception.getLocation().getLineNr() : 1; + int line = (exception.getLocation() != null) ? exception.getLocation().getLineNr() + 1 : 1; return new SwaggerError(line, IMarker.SEVERITY_ERROR, 0, exception.getMessage()); } diff --git a/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/validation/ValidationUtil.java b/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/validation/ValidationUtil.java index f2d940fd..aa37e29c 100644 --- a/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/validation/ValidationUtil.java +++ b/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/validation/ValidationUtil.java @@ -10,21 +10,13 @@ *******************************************************************************/ package com.reprezen.swagedit.core.validation; -import java.util.Arrays; -import java.util.LinkedList; -import java.util.List; - -import org.yaml.snakeyaml.nodes.MappingNode; -import org.yaml.snakeyaml.nodes.Node; -import org.yaml.snakeyaml.nodes.NodeTuple; -import org.yaml.snakeyaml.nodes.ScalarNode; - +import com.fasterxml.jackson.core.JsonPointer; import com.fasterxml.jackson.databind.JsonNode; -import com.google.common.base.Strings; -import com.google.common.collect.Lists; +import com.reprezen.swagedit.core.json.JsonModel; +import com.reprezen.swagedit.core.json.JsonRegion; public class ValidationUtil { - + public static boolean isInDefinition(String pointerString) { return isInOpenApi3Definition(pointerString) // || isInSwagger2Definition(pointerString); @@ -38,7 +30,7 @@ private static boolean isInSwagger2Definition(String pointerString) { private static boolean isInOpenApi3Definition(String pointerString) { return pointerString.startsWith("/components/schemas"); // OAS v3 } - + public static String getInstancePointer(JsonNode error) { if (!error.has("instance") || !error.get("instance").has("pointer")) return null; @@ -56,93 +48,24 @@ public static String getInstancePointer(JsonNode error) { * * The Node matching the path is found by the methods findNode(). */ - public static int getLine(JsonNode error, Node yamlTree) { + public static int getLine(JsonNode error, JsonModel model) { String path = getInstancePointer(error); if (path == null || path.isEmpty()) return 1; - path = path.substring(1, path.length()); - String[] strings = path.split("/"); + JsonRegion node = model.getRanges().get(JsonPointer.compile(path)); - if (yamlTree instanceof MappingNode) { - MappingNode mn = (MappingNode) yamlTree; - - Node findNode = findNode(mn, Arrays.asList(strings)); - if (findNode != null) { - return findNode.getStartMark().getLine() + 1; + int line = 1; + if (node != null) { + if (node.getFieldLocation() != null) { + line = node.getFieldLocation().startLine; + } else { + line = node.getContentLocation().startLine; } } - return 1; - } - - /* - * Returns the yaml node that matches the given path. - * - * The path is given as a list of String. The Node matching the path is found by traversing the children of the node - * pass as first parameter. - */ - private static Node findNode(MappingNode root, List paths) { - if (paths.isEmpty()) - return root; - - String path = paths.get(0); - if (path.startsWith("/")) { - path = path.substring(1, path.length()); - } - - final List next = paths.subList(1, paths.size()); - // ~1 is use to escape / - if (path.contains("~1")) { - path = path.replaceAll("~1", "/"); - } - - for (NodeTuple child : root.getValue()) { - if (child.getKeyNode() instanceof ScalarNode) { - ScalarNode scalar = (ScalarNode) child.getKeyNode(); - - if (scalar.getValue().equals(path)) { - return findNode(child, next); - } - } - } - - return root; - } - - private static Node findNode(NodeTuple child, List paths) { - if (child.getValueNode() instanceof MappingNode) { - return findNode((MappingNode) child.getValueNode(), paths); - } - return child.getKeyNode(); - } - - public static JsonNode findNode(String path, JsonNode root) { - return findNode(Lists.newLinkedList(Arrays.asList(path.split("/"))), root); - } - - private static JsonNode findNode(LinkedList path, JsonNode root) { - if (root == null) { - return null; - } - // retrieves the first element, and also *removes* it - String firstSegment = path.pop(); - if (Strings.isNullOrEmpty(firstSegment)) { - return findNode(path, root); - } - int firstSegmentAsNumber = -1; - try { - firstSegmentAsNumber = Integer.parseInt(firstSegment); - } catch (NumberFormatException e) { - // ignore - } - JsonNode nodeForSegment = firstSegmentAsNumber == -1 ? root.get(firstSegment) : root - .get(firstSegmentAsNumber); - if (path.isEmpty()) { - return nodeForSegment; - } - return findNode(path, nodeForSegment); + return line; } } diff --git a/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/validation/Validator.java b/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/validation/Validator.java index c72977c9..54878c2f 100644 --- a/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/validation/Validator.java +++ b/com.reprezen.swagedit.core/src/com/reprezen/swagedit/core/validation/Validator.java @@ -12,45 +12,26 @@ import java.io.IOException; import java.net.URI; -import java.util.HashSet; import java.util.Map; -import java.util.Objects; import java.util.Set; -import org.apache.commons.lang3.tuple.Pair; import org.dadacoalition.yedit.YEditLog; -import org.eclipse.core.resources.IMarker; import org.eclipse.ui.IFileEditorInput; -import org.yaml.snakeyaml.nodes.MappingNode; -import org.yaml.snakeyaml.nodes.Node; -import org.yaml.snakeyaml.nodes.NodeId; -import org.yaml.snakeyaml.nodes.NodeTuple; -import org.yaml.snakeyaml.nodes.ScalarNode; -import org.yaml.snakeyaml.nodes.SequenceNode; import org.yaml.snakeyaml.parser.ParserException; -import com.fasterxml.jackson.core.JsonPointer; import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; import com.github.fge.jsonschema.core.exceptions.ProcessingException; import com.github.fge.jsonschema.core.load.configuration.LoadingConfiguration; import com.github.fge.jsonschema.core.load.configuration.LoadingConfigurationBuilder; import com.github.fge.jsonschema.core.report.ProcessingReport; import com.github.fge.jsonschema.main.JsonSchema; import com.github.fge.jsonschema.main.JsonSchemaFactory; -import com.google.common.collect.HashMultimap; import com.google.common.collect.Maps; -import com.google.common.collect.Multimap; import com.google.common.collect.Sets; import com.reprezen.swagedit.core.editor.JsonDocument; -import com.reprezen.swagedit.core.json.references.JsonReference; +import com.reprezen.swagedit.core.json.JsonModel; import com.reprezen.swagedit.core.json.references.JsonReferenceFactory; import com.reprezen.swagedit.core.json.references.JsonReferenceValidator; -import com.reprezen.swagedit.core.model.AbstractNode; -import com.reprezen.swagedit.core.model.ArrayNode; -import com.reprezen.swagedit.core.model.Model; -import com.reprezen.swagedit.core.model.ObjectNode; -import com.reprezen.swagedit.core.model.ValueNode; /** * This class contains methods for validating a Swagger YAML document. @@ -62,8 +43,6 @@ public class Validator { private final JsonReferenceValidator referenceValidator; - private final JsonNode schemaRefTemplate = new ObjectMapper().createObjectNode() // - .put("$ref", "#/definitions/schema"); private final LoadingConfiguration loadingConfiguration; private final JsonSchemaFactory factory; @@ -84,7 +63,6 @@ public Validator(JsonReferenceValidator referenceValidator, Map preloadSchemas) { LoadingConfigurationBuilder loadingConfigurationBuilder = LoadingConfiguration.newBuilder(); for (String nextSchemaUri : preloadSchemas.keySet()) { @@ -97,6 +75,10 @@ public JsonSchemaFactory getFactory() { return factory; } + public ModelValidator getModelValidator(JsonModel model) { + return new ModelValidator(model); + } + /** * Returns a list or errors if validation fails. * @@ -113,31 +95,29 @@ public Set validate(JsonDocument document, IFileEditorInput editor URI baseURI = editorInput != null ? editorInput.getFile().getLocationURI() : null; return validate(document, baseURI); } - + public Set validate(JsonDocument document, URI baseURI) { Set errors = Sets.newHashSet(); - JsonNode jsonContent = null; + JsonModel model; try { - jsonContent = document.asJson(); + model = document.getContent(); } catch (Exception e) { - jsonContent = null; + model = null; } - if (jsonContent != null) { - Node yaml = document.getYaml(); - if (yaml != null) { - errors.addAll(validateAgainstSchema( - new ErrorProcessor(yaml, document.getSchema().getRootType().getContent()), document)); - errors.addAll(validateModel(document.getModel())); - errors.addAll(checkDuplicateKeys(yaml)); - errors.addAll(referenceValidator.validate(baseURI, document)); - } + if (model != null) { + ErrorProcessor processor = new ErrorProcessor(model, document.getSchema().getRootType().getContent()); + + errors.addAll(model.getErrors()); + errors.addAll(validateAgainstSchema(processor, document)); + errors.addAll(getModelValidator(model).validateModel()); + errors.addAll(referenceValidator.validate(baseURI, document)); } return errors; } - + /** * Validates the YAML document against the Swagger schema * @@ -148,8 +128,9 @@ public Set validate(JsonDocument document, URI baseURI) { protected Set validateAgainstSchema(ErrorProcessor processor, JsonDocument document) { return validateAgainstSchema(processor, document.getSchema().asJson(), document.asJson()); } - - public Set validateAgainstSchema(ErrorProcessor processor, JsonNode schemaAsJson, JsonNode documentAsJson) { + + public Set validateAgainstSchema(ErrorProcessor processor, JsonNode schemaAsJson, + JsonNode documentAsJson) { final Set errors = Sets.newHashSet(); JsonSchema schema = null; @@ -171,201 +152,4 @@ public Set validateAgainstSchema(ErrorProcessor processor, JsonNod return errors; } - /** - * Validates the model against with different rules that cannot be verified only by JSON schema validation. - * - * @param model - * @return errors - */ - protected Set validateModel(Model model) { - final Set errors = new HashSet<>(); - - if (model != null && model.getRoot() != null) { - for (AbstractNode node : model.allNodes()) { - executeModelValidation(model, node, errors); - } - } - return errors; - } - - protected void executeModelValidation(Model model, AbstractNode node, Set errors) { - checkArrayTypeDefinition(errors, node); - checkObjectTypeDefinition(errors, node); - } - - /** - * This method checks that the node if an array type definitions includes an items field. - * - * @param errors - * @param model - */ - protected void checkArrayTypeDefinition(Set errors, AbstractNode node) { - if (hasArrayType(node)) { - AbstractNode items = node.get("items"); - if (items == null) { - errors.add(error(node, IMarker.SEVERITY_ERROR, Messages.error_array_missing_items)); - } else { - if (!items.isObject()) { - errors.add(error(items, IMarker.SEVERITY_ERROR, Messages.error_array_items_should_be_object)); - } - } - } - } - - /** - * Returns true if the node is an array type definition - * - * @param node - * @return true if array definition - */ - protected boolean hasArrayType(AbstractNode node) { - if (node.isObject() && node.get("type") instanceof ValueNode) { - ValueNode typeValue = node.get("type").asValue(); - return typeValue.getValue() != null && "array".equalsIgnoreCase(typeValue.getValue().toString()); - } - return false; - } - - /** - * Validates an object type definition. - * - * @param errors - * @param node - */ - protected void checkObjectTypeDefinition(Set errors, AbstractNode node) { - if (node instanceof ObjectNode) { - JsonPointer ptr = node.getPointer(); - if (ptr != null && ValidationUtil.isInDefinition(ptr.toString())) { - checkMissingType(errors, node); - checkMissingRequiredProperties(errors, node); - } - } - } - - /** - * This method checks that the node if an object definition includes a type field. - * - * @param errors - * @param node - */ - protected void checkMissingType(Set errors, AbstractNode node) { - // object - if (node.get("properties") != null) { - // bypass this node, it is a property whose name is `properties` - - if ("properties".equals(node.getProperty())) { - return; - } - - if (node.get("type") == null) { - errors.add(error(node, IMarker.SEVERITY_WARNING, Messages.error_object_type_missing)); - } else { - AbstractNode typeValue = node.get("type"); - if (!(typeValue instanceof ValueNode) || !Objects.equals("object", typeValue.asValue().getValue())) { - errors.add(error(node, IMarker.SEVERITY_ERROR, Messages.error_wrong_type)); - } - } - } else if (isSchemaDefinition(node) && node.get("type") == null) { - errors.add(error(node, IMarker.SEVERITY_WARNING, Messages.error_type_missing)); - } - } - - private boolean isSchemaDefinition(AbstractNode node) { - // need to use getContent() because asJson() returns resolvedValue is some subclasses - return schemaRefTemplate.equals(node.getType().getContent()) // - && node.get(JsonReference.PROPERTY) == null // - && node.get("allOf") == null; - } - - /** - * This method checks that the required values for the object type definition contains only valid properties. - * - * @param errors - * @param node - */ - protected void checkMissingRequiredProperties(Set errors, AbstractNode node) { - if (node.get("required") instanceof ArrayNode) { - ArrayNode required = node.get("required").asArray(); - - AbstractNode properties = node.get("properties"); - if (properties == null) { - errors.add(error(node, IMarker.SEVERITY_ERROR, Messages.error_missing_properties)); - } else { - for (AbstractNode prop : required.elements()) { - if (prop instanceof ValueNode) { - ValueNode valueNode = prop.asValue(); - String value = valueNode.getValue().toString(); - - if (properties.get(value) == null) { - errors.add(error(valueNode, IMarker.SEVERITY_ERROR, - String.format(Messages.error_required_properties, value))); - } - } - } - } - } - } - - protected SwaggerError error(AbstractNode node, int level, String message) { - return new SwaggerError(node.getStart().getLine() + 1, level, message); - } - - /* - * Finds all duplicate keys in all objects present in the YAML document. - */ - protected Set checkDuplicateKeys(Node document) { - HashMultimap, Node> acc = HashMultimap., Node> create(); - - collectDuplicates(document, acc); - - Set errors = Sets.newHashSet(); - for (Pair key : acc.keys()) { - Set duplicates = acc.get(key); - - if (duplicates.size() > 1) { - for (Node duplicate : duplicates) { - errors.add(createDuplicateError(key.getValue(), duplicate)); - } - } - } - - return errors; - } - - /* - * This method iterates through the YAML tree to collect the pairs of Node x String representing an object and one - * of it's keys. Each pair is associated to a Set of Nodes which contains all nodes being a key to the pair's Node - * and having for value the pair's key. Once the iteration is done, the resulting map should be traversed. Each pair - * having more than one element in its associated Set are duplicate keys. - */ - protected void collectDuplicates(Node parent, Multimap, Node> acc) { - switch (parent.getNodeId()) { - case mapping: { - for (NodeTuple value : ((MappingNode) parent).getValue()) { - Node keyNode = value.getKeyNode(); - - if (keyNode.getNodeId() == NodeId.scalar) { - acc.put(Pair.of(parent, ((ScalarNode) keyNode).getValue()), keyNode); - } - - collectDuplicates(value.getValueNode(), acc); - } - } - break; - case sequence: { - for (Node value : ((SequenceNode) parent).getValue()) { - collectDuplicates(value, acc); - } - } - break; - default: - break; - } - } - - protected SwaggerError createDuplicateError(String key, Node node) { - return new SwaggerError(node.getStartMark().getLine() + 1, IMarker.SEVERITY_WARNING, - String.format(Messages.error_duplicate_keys, key)); - } - } diff --git a/com.reprezen.swagedit.openapi3.tests/src/com/reprezen/swagedit/openapi3/assist/CodeAssistHelper.xtend b/com.reprezen.swagedit.openapi3.tests/src/com/reprezen/swagedit/openapi3/assist/CodeAssistHelper.xtend index f2bdffde..dd1d5cd9 100644 --- a/com.reprezen.swagedit.openapi3.tests/src/com/reprezen/swagedit/openapi3/assist/CodeAssistHelper.xtend +++ b/com.reprezen.swagedit.openapi3.tests/src/com/reprezen/swagedit/openapi3/assist/CodeAssistHelper.xtend @@ -94,12 +94,12 @@ class CodeAssistHelper { val line = document.getLineOfOffset(offset) val annotationLine = document.get(region.offset, region.getLength()) - val path = document.getModel(offset).getPath(line, document.getColumnOfOffset(line, region)) + val path = document.getPath(line, document.getColumnOfOffset(line, region)) val isArrayItem = annotationLine.contains(" " + arrayItemMarker) val maybeArrayPrefix = if(isArrayItem) "/0" else "" - val contextType = new OpenApi3ContextTypeProvider().getContextType(document.getModel(), path.toString + maybeArrayPrefix) + val contextType = new OpenApi3ContextTypeProvider().getContextType(document, path.toString + maybeArrayPrefix) return contextType } } diff --git a/com.reprezen.swagedit.openapi3.tests/src/com/reprezen/swagedit/openapi3/assist/OpenApi3ContentAssistProcessorTest.xtend b/com.reprezen.swagedit.openapi3.tests/src/com/reprezen/swagedit/openapi3/assist/OpenApi3ContentAssistProcessorTest.xtend index a261a491..fab8a525 100644 --- a/com.reprezen.swagedit.openapi3.tests/src/com/reprezen/swagedit/openapi3/assist/OpenApi3ContentAssistProcessorTest.xtend +++ b/com.reprezen.swagedit.openapi3.tests/src/com/reprezen/swagedit/openapi3/assist/OpenApi3ContentAssistProcessorTest.xtend @@ -11,7 +11,9 @@ package com.reprezen.swagedit.openapi3.assist import com.reprezen.swagedit.core.assist.StyledCompletionProposal +import com.reprezen.swagedit.core.editor.JsonDocument import com.reprezen.swagedit.openapi3.editor.OpenApi3Document +import com.reprezen.swagedit.openapi3.schema.OpenApi3Schema import java.util.ArrayList import org.junit.Test @@ -19,19 +21,18 @@ import static com.reprezen.swagedit.openapi3.utils.Cursors.* import static org.hamcrest.core.IsCollectionContaining.* import static org.hamcrest.core.IsNot.* import static org.junit.Assert.* -import com.reprezen.swagedit.openapi3.schema.OpenApi3Schema -import com.reprezen.swagedit.core.model.Model +import com.reprezen.swagedit.core.json.JsonModel class OpenApi3ContentAssistProcessorTest { val processor = new OpenApi3ContentAssistProcessor(null, new OpenApi3Schema) { - override protected initTextMessages(Model model) { new ArrayList } + override protected initTextMessages(JsonModel doc) { new ArrayList } override protected getContextTypeRegistry() { null } override protected getTemplateStore() { null } - override protected getContextTypeId(Model model, String path) { null } + override protected getContextTypeId(JsonDocument doc, String path) { null } } @Test diff --git a/com.reprezen.swagedit.openapi3.tests/src/com/reprezen/swagedit/openapi3/assist/ReferenceContextTest.xtend b/com.reprezen.swagedit.openapi3.tests/src/com/reprezen/swagedit/openapi3/assist/ReferenceContextTest.xtend index 55087857..1769feca 100644 --- a/com.reprezen.swagedit.openapi3.tests/src/com/reprezen/swagedit/openapi3/assist/ReferenceContextTest.xtend +++ b/com.reprezen.swagedit.openapi3.tests/src/com/reprezen/swagedit/openapi3/assist/ReferenceContextTest.xtend @@ -60,10 +60,10 @@ class ReferenceContextTest { val line = document.getLineOfOffset(offset) val annotationLine = document.get(region.offset, region.getLength()) - val path = document.getModel(offset).getPath(line, document.getColumnOfOffset(line, region)) + val path = document.getPath(line, document.getColumnOfOffset(line, region)) val isArrayItem = annotationLine.contains(" " + arrayItemMarker) val maybeArrayPrefix = if (isArrayItem) "/0" else "" - val contextType = allContextTypes.get(document.getModel(), path.append(JsonPointer.compile(maybeArrayPrefix + "/$ref"))) + val contextType = allContextTypes.get(document.content, path.append(JsonPointer.compile(maybeArrayPrefix + "/$ref"))) val matcher = refValuePattern.matcher(annotationLine) diff --git a/com.reprezen.swagedit.openapi3.tests/src/com/reprezen/swagedit/openapi3/utils/Mocks.java b/com.reprezen.swagedit.openapi3.tests/src/com/reprezen/swagedit/openapi3/utils/Mocks.java index 25b0b2cc..3f65e8fd 100644 --- a/com.reprezen.swagedit.openapi3.tests/src/com/reprezen/swagedit/openapi3/utils/Mocks.java +++ b/com.reprezen.swagedit.openapi3.tests/src/com/reprezen/swagedit/openapi3/utils/Mocks.java @@ -25,7 +25,6 @@ import com.fasterxml.jackson.databind.JsonNode; import com.reprezen.swagedit.core.json.references.JsonDocumentManager; import com.reprezen.swagedit.core.json.references.JsonReference; -import com.reprezen.swagedit.core.model.AbstractNode; import com.reprezen.swagedit.openapi3.editor.OpenApi3Document; import com.reprezen.swagedit.openapi3.validation.OpenApi3ReferenceValidator; import com.reprezen.swagedit.openapi3.validation.OpenApi3ReferenceValidator.OpenApi3ReferenceFactory; @@ -43,7 +42,7 @@ public static OpenApi3ReferenceFactory mockJsonReferenceFactory(final Map fromNode(JsonNode error, int indent) { @@ -52,7 +51,7 @@ class ValidationHelper { } } val validator = new OpenApi3ReferenceValidator(new OpenApi3ReferenceFactory() { - override create(AbstractNode node) { + override create(JsonNode node) { val reference = super.create(node) if (reference !== null) { reference.documentManager = docManager @@ -60,8 +59,8 @@ class ValidationHelper { reference } - override createSimpleReference(URI baseURI, AbstractNode valueNode) { - val reference = super.createSimpleReference(baseURI, valueNode) + override createSimpleReference(URI baseURI, JsonNode doc, JsonNode valueNode) { + val reference = super.createSimpleReference(baseURI, doc, valueNode) if (reference !== null) { reference.documentManager = docManager } diff --git a/com.reprezen.swagedit.openapi3/META-INF/MANIFEST.MF b/com.reprezen.swagedit.openapi3/META-INF/MANIFEST.MF index af5005b9..6e2e2a17 100644 --- a/com.reprezen.swagedit.openapi3/META-INF/MANIFEST.MF +++ b/com.reprezen.swagedit.openapi3/META-INF/MANIFEST.MF @@ -15,11 +15,6 @@ Require-Bundle: org.eclipse.ui, org.eclipse.core.resources, com.github.eclipsecolortheme;resolution:=optional, com.google.guava;bundle-version="15.0.0", - com.fasterxml.jackson.core.jackson-annotations;bundle-version="2.5.0", - com.fasterxml.jackson.core.jackson-core;bundle-version="2.5.0", - com.fasterxml.jackson.core.jackson-databind;bundle-version="2.5.0", - com.fasterxml.jackson.dataformat.jackson-dataformat-yaml;bundle-version="2.5.0", - org.yaml.snakeyaml;bundle-version="1.14.0", org.slf4j.api;bundle-version="1.7.2" Bundle-RequiredExecutionEnvironment: JavaSE-1.7 Bundle-ActivationPolicy: lazy diff --git a/com.reprezen.swagedit.openapi3/src/com/reprezen/swagedit/openapi3/assist/OpenApi3ContentAssistProcessor.java b/com.reprezen.swagedit.openapi3/src/com/reprezen/swagedit/openapi3/assist/OpenApi3ContentAssistProcessor.java index 1e0c778a..c9995bc4 100644 --- a/com.reprezen.swagedit.openapi3/src/com/reprezen/swagedit/openapi3/assist/OpenApi3ContentAssistProcessor.java +++ b/com.reprezen.swagedit.openapi3/src/com/reprezen/swagedit/openapi3/assist/OpenApi3ContentAssistProcessor.java @@ -19,7 +19,7 @@ import com.reprezen.swagedit.core.assist.JsonProposalProvider; import com.reprezen.swagedit.core.assist.ext.MediaTypeContentAssistExt; import com.reprezen.swagedit.core.assist.ext.ResponseCodeContentAssistExt; -import com.reprezen.swagedit.core.model.Model; +import com.reprezen.swagedit.core.editor.JsonDocument; import com.reprezen.swagedit.core.schema.CompositeSchema; import com.reprezen.swagedit.openapi3.Activator; import com.reprezen.swagedit.openapi3.assist.ext.CallbacksContentAssistExt; @@ -56,8 +56,9 @@ protected ContextTypeRegistry getContextTypeRegistry() { } @Override - protected String getContextTypeId(Model model, String path) { - TemplateContextType contextType = Activator.getDefault().getOpenApi3ContextTypeProvider().getContextType(model, path); + protected String getContextTypeId(JsonDocument doc, String path) { + TemplateContextType contextType = Activator.getDefault().getOpenApi3ContextTypeProvider().getContextType(doc, + path); return contextType != null ? contextType.getId() : null; } diff --git a/com.reprezen.swagedit.openapi3/src/com/reprezen/swagedit/openapi3/assist/contexts/OperationContextType.java b/com.reprezen.swagedit.openapi3/src/com/reprezen/swagedit/openapi3/assist/contexts/OperationContextType.java index d6e8dc27..413fad3f 100644 --- a/com.reprezen.swagedit.openapi3/src/com/reprezen/swagedit/openapi3/assist/contexts/OperationContextType.java +++ b/com.reprezen.swagedit.openapi3/src/com/reprezen/swagedit/openapi3/assist/contexts/OperationContextType.java @@ -11,7 +11,6 @@ package com.reprezen.swagedit.openapi3.assist.contexts; import java.util.Collection; -import java.util.List; import org.eclipse.core.runtime.IPath; @@ -19,10 +18,8 @@ import com.google.common.collect.Lists; import com.reprezen.swagedit.core.assist.Proposal; import com.reprezen.swagedit.core.assist.contexts.SchemaContextType; -import com.reprezen.swagedit.core.model.AbstractNode; -import com.reprezen.swagedit.core.model.Model; +import com.reprezen.swagedit.core.json.JsonModel; import com.reprezen.swagedit.core.schema.CompositeSchema; -import com.reprezen.swagedit.core.utils.URLUtils; /** * ContextType that collects proposals from operations pointers. @@ -36,19 +33,19 @@ public OperationContextType(CompositeSchema schema, String regex) { } @Override - public Collection collectProposals(Model model, IPath path) { + public Collection collectProposals(JsonModel document, IPath path) { final Collection results = Lists.newArrayList(); - final List nodes = model.findByType(operationPointer); - - for (AbstractNode node : nodes) { - String pointer = node.getPointerString(); - String basePath = (path != null ? path.toString() : "") + "#" + pointer; - String key = node.getProperty(); - String value = basePath; - String encoded = URLUtils.encodeURL(value); - - results.add(new Proposal("\"" + encoded + "\"", key, null, value)); - } + // final List nodes = document.findByType(operationPointer); + // + // for (JsonNode node : nodes) { + // String pointer = node.getPointerString(); + // String basePath = (path != null ? path.toString() : "") + "#" + pointer; + // String key = node.getProperty(); + // String value = basePath; + // String encoded = URLUtils.encodeURL(value); + // + // results.add(new Proposal("\"" + encoded + "\"", key, null, value)); + // } return results; } diff --git a/com.reprezen.swagedit.openapi3/src/com/reprezen/swagedit/openapi3/assist/contexts/OperationIdContextType.java b/com.reprezen.swagedit.openapi3/src/com/reprezen/swagedit/openapi3/assist/contexts/OperationIdContextType.java index c3982811..371c12b1 100644 --- a/com.reprezen.swagedit.openapi3/src/com/reprezen/swagedit/openapi3/assist/contexts/OperationIdContextType.java +++ b/com.reprezen.swagedit.openapi3/src/com/reprezen/swagedit/openapi3/assist/contexts/OperationIdContextType.java @@ -11,7 +11,6 @@ package com.reprezen.swagedit.openapi3.assist.contexts; import java.util.Collection; -import java.util.List; import org.eclipse.core.runtime.IPath; @@ -19,8 +18,7 @@ import com.google.common.collect.Lists; import com.reprezen.swagedit.core.assist.Proposal; import com.reprezen.swagedit.core.assist.contexts.SchemaContextType; -import com.reprezen.swagedit.core.model.AbstractNode; -import com.reprezen.swagedit.core.model.Model; +import com.reprezen.swagedit.core.json.JsonModel; import com.reprezen.swagedit.core.schema.CompositeSchema; /** @@ -35,18 +33,18 @@ public OperationIdContextType(CompositeSchema schema, String regex) { } @Override - public Collection collectProposals(Model model, IPath path) { + public Collection collectProposals(JsonModel document, IPath path) { final Collection results = Lists.newArrayList(); - final List nodes = model.findByType(operationPointer); - - for (AbstractNode node : nodes) { - AbstractNode value = node.get("operationId"); - if (value != null && value.asValue().getValue() instanceof String) { - String key = (String) value.asValue().getValue(); - - results.add(new Proposal(key, key, null, value.getProperty())); - } - } + // final List nodes = model.findByType(operationPointer); + // + // for (AbstractNode node : nodes) { + // AbstractNode value = node.get("operationId"); + // if (value != null && value.asValue().getValue() instanceof String) { + // String key = (String) value.asValue().getValue(); + // + // results.add(new Proposal(key, key, null, value.getProperty())); + // } + // } return results; } } \ No newline at end of file diff --git a/com.reprezen.swagedit.openapi3/src/com/reprezen/swagedit/openapi3/assist/contexts/SecuritySchemeContextType.java b/com.reprezen.swagedit.openapi3/src/com/reprezen/swagedit/openapi3/assist/contexts/SecuritySchemeContextType.java index 55de3631..fd1a7595 100644 --- a/com.reprezen.swagedit.openapi3/src/com/reprezen/swagedit/openapi3/assist/contexts/SecuritySchemeContextType.java +++ b/com.reprezen.swagedit.openapi3/src/com/reprezen/swagedit/openapi3/assist/contexts/SecuritySchemeContextType.java @@ -18,8 +18,7 @@ import com.google.common.collect.Lists; import com.reprezen.swagedit.core.assist.Proposal; import com.reprezen.swagedit.core.assist.contexts.SchemaContextType; -import com.reprezen.swagedit.core.model.AbstractNode; -import com.reprezen.swagedit.core.model.Model; +import com.reprezen.swagedit.core.json.JsonModel; import com.reprezen.swagedit.core.schema.CompositeSchema; /** @@ -34,15 +33,15 @@ public SecuritySchemeContextType(CompositeSchema schema, String regex) { } @Override - public Collection collectProposals(Model model, IPath path) { + public Collection collectProposals(JsonModel document, IPath path) { final Collection results = Lists.newArrayList(); - AbstractNode securitySchemes = model.find(securityPointer); - - if (securitySchemes != null && securitySchemes.isObject()) { - for (String key : securitySchemes.asObject().fieldNames()) { - results.add(new Proposal(key, key, null, securitySchemes.getProperty())); - } - } + // AbstractNode securitySchemes = model.find(securityPointer); + // + // if (securitySchemes != null && securitySchemes.isObject()) { + // for (String key : securitySchemes.asObject().fieldNames()) { + // results.add(new Proposal(key, key, null, securitySchemes.getProperty())); + // } + // } return results; } diff --git a/com.reprezen.swagedit.openapi3/src/com/reprezen/swagedit/openapi3/assist/ext/CallbacksContentAssistExt.java b/com.reprezen.swagedit.openapi3/src/com/reprezen/swagedit/openapi3/assist/ext/CallbacksContentAssistExt.java index e25f17b1..f987e28a 100644 --- a/com.reprezen.swagedit.openapi3/src/com/reprezen/swagedit/openapi3/assist/ext/CallbacksContentAssistExt.java +++ b/com.reprezen.swagedit.openapi3/src/com/reprezen/swagedit/openapi3/assist/ext/CallbacksContentAssistExt.java @@ -13,10 +13,10 @@ import java.util.Collection; import com.fasterxml.jackson.core.JsonPointer; +import com.fasterxml.jackson.databind.JsonNode; import com.google.common.collect.Lists; import com.reprezen.swagedit.core.assist.Proposal; import com.reprezen.swagedit.core.assist.ext.ContentAssistExt; -import com.reprezen.swagedit.core.model.AbstractNode; import com.reprezen.swagedit.core.schema.TypeDefinition; public class CallbacksContentAssistExt implements ContentAssistExt { @@ -29,7 +29,7 @@ public boolean canProvideContentAssist(TypeDefinition type) { } @Override - public Collection getProposals(TypeDefinition type, AbstractNode node, String prefix) { + public Collection getProposals(TypeDefinition type, JsonNode node, String prefix) { return Lists.newArrayList( // new Proposal("x-:", "x-", null, "specificationExtension")); } diff --git a/com.reprezen.swagedit.openapi3/src/com/reprezen/swagedit/openapi3/assist/ext/ParameterInContentAssistExt.java b/com.reprezen.swagedit.openapi3/src/com/reprezen/swagedit/openapi3/assist/ext/ParameterInContentAssistExt.java index f614d7ce..ebb129de 100644 --- a/com.reprezen.swagedit.openapi3/src/com/reprezen/swagedit/openapi3/assist/ext/ParameterInContentAssistExt.java +++ b/com.reprezen.swagedit.openapi3/src/com/reprezen/swagedit/openapi3/assist/ext/ParameterInContentAssistExt.java @@ -13,10 +13,10 @@ import java.util.Collection; import com.fasterxml.jackson.core.JsonPointer; +import com.fasterxml.jackson.databind.JsonNode; import com.google.common.collect.Lists; import com.reprezen.swagedit.core.assist.Proposal; import com.reprezen.swagedit.core.assist.ext.ContentAssistExt; -import com.reprezen.swagedit.core.model.AbstractNode; import com.reprezen.swagedit.core.schema.TypeDefinition; public class ParameterInContentAssistExt implements ContentAssistExt { @@ -30,7 +30,7 @@ public boolean canProvideContentAssist(TypeDefinition type) { } @Override - public Collection getProposals(TypeDefinition type, AbstractNode node, String prefix) { + public Collection getProposals(TypeDefinition type, JsonNode node, String prefix) { return Lists.newArrayList( // new Proposal("query", "query", description, "string"), new Proposal("header", "header", description, "string"), diff --git a/com.reprezen.swagedit.openapi3/src/com/reprezen/swagedit/openapi3/assist/ext/SchemaFormatContentAssistExt.java b/com.reprezen.swagedit.openapi3/src/com/reprezen/swagedit/openapi3/assist/ext/SchemaFormatContentAssistExt.java index 95903b87..ef6c83e9 100644 --- a/com.reprezen.swagedit.openapi3/src/com/reprezen/swagedit/openapi3/assist/ext/SchemaFormatContentAssistExt.java +++ b/com.reprezen.swagedit.openapi3/src/com/reprezen/swagedit/openapi3/assist/ext/SchemaFormatContentAssistExt.java @@ -15,12 +15,12 @@ import java.util.Map; import com.fasterxml.jackson.core.JsonPointer; +import com.fasterxml.jackson.databind.JsonNode; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Lists; import com.reprezen.swagedit.core.assist.Proposal; import com.reprezen.swagedit.core.assist.ext.ContentAssistExt; -import com.reprezen.swagedit.core.model.AbstractNode; import com.reprezen.swagedit.core.schema.TypeDefinition; public class SchemaFormatContentAssistExt implements ContentAssistExt { @@ -56,22 +56,22 @@ public boolean canProvideContentAssist(TypeDefinition type) { } @Override - public Collection getProposals(TypeDefinition type, AbstractNode node, String prefix) { + public Collection getProposals(TypeDefinition type, JsonNode node, String prefix) { List proposals = Lists.newArrayList(); - - if (node.getParent() != null && node.getParent().get("type") != null) { - String filter = (String) node.getParent().get("type").asValue().getValue(); - - if (values.containsKey(filter)) { - return values.get(filter); - } - } - - for (List value : values.values()) { - for (Proposal v : value) { - proposals.add(v); - } - } + // + // if (node.getParent() != null && node.getParent().get("type") != null) { + // String filter = (String) node.getParent().get("type").asValue().getValue(); + // + // if (values.containsKey(filter)) { + // return values.get(filter); + // } + // } + // + // for (List value : values.values()) { + // for (Proposal v : value) { + // proposals.add(v); + // } + // } proposals.add(new Proposal("", "", null, "string")); diff --git a/com.reprezen.swagedit.openapi3/src/com/reprezen/swagedit/openapi3/assist/ext/SchemaTypeContentAssistExt.java b/com.reprezen.swagedit.openapi3/src/com/reprezen/swagedit/openapi3/assist/ext/SchemaTypeContentAssistExt.java index 5319597a..3752c2c5 100644 --- a/com.reprezen.swagedit.openapi3/src/com/reprezen/swagedit/openapi3/assist/ext/SchemaTypeContentAssistExt.java +++ b/com.reprezen.swagedit.openapi3/src/com/reprezen/swagedit/openapi3/assist/ext/SchemaTypeContentAssistExt.java @@ -13,10 +13,10 @@ import java.util.Collection; import com.fasterxml.jackson.core.JsonPointer; +import com.fasterxml.jackson.databind.JsonNode; import com.google.common.collect.Lists; import com.reprezen.swagedit.core.assist.Proposal; import com.reprezen.swagedit.core.assist.ext.ContentAssistExt; -import com.reprezen.swagedit.core.model.AbstractNode; import com.reprezen.swagedit.core.schema.TypeDefinition; public class SchemaTypeContentAssistExt implements ContentAssistExt { @@ -29,7 +29,7 @@ public boolean canProvideContentAssist(TypeDefinition type) { } @Override - public Collection getProposals(TypeDefinition type, AbstractNode node, String prefix) { + public Collection getProposals(TypeDefinition type, JsonNode node, String prefix) { return Lists.newArrayList( // new Proposal("array", "array", null, "enum"), // new Proposal("boolean", "boolean", null, "enum"), // diff --git a/com.reprezen.swagedit.openapi3/src/com/reprezen/swagedit/openapi3/editor/OpenApi3Document.java b/com.reprezen.swagedit.openapi3/src/com/reprezen/swagedit/openapi3/editor/OpenApi3Document.java index 2e54a52d..1ab0fe69 100644 --- a/com.reprezen.swagedit.openapi3/src/com/reprezen/swagedit/openapi3/editor/OpenApi3Document.java +++ b/com.reprezen.swagedit.openapi3/src/com/reprezen/swagedit/openapi3/editor/OpenApi3Document.java @@ -10,8 +10,6 @@ *******************************************************************************/ package com.reprezen.swagedit.openapi3.editor; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; import com.reprezen.swagedit.core.editor.JsonDocument; import com.reprezen.swagedit.openapi3.Activator; import com.reprezen.swagedit.openapi3.schema.OpenApi3Schema; @@ -27,7 +25,7 @@ public OpenApi3Document(OpenApi3Schema schema) { // It's done to eliminate an initialization exception here - // Jackson Dataformat library does not declare an OSGi dependency on Jackson Databind which is needed to // initialize YAMLMapper (it extends ObjectMapper from Jackson Databind). - super(new ObjectMapper(new YAMLFactory()), schema); + super(schema); } } diff --git a/com.reprezen.swagedit.openapi3/src/com/reprezen/swagedit/openapi3/hyperlinks/LinkOperationHyperlinkDetector.java b/com.reprezen.swagedit.openapi3/src/com/reprezen/swagedit/openapi3/hyperlinks/LinkOperationHyperlinkDetector.java index 8344b1b6..80ddb8db 100644 --- a/com.reprezen.swagedit.openapi3/src/com/reprezen/swagedit/openapi3/hyperlinks/LinkOperationHyperlinkDetector.java +++ b/com.reprezen.swagedit.openapi3/src/com/reprezen/swagedit/openapi3/hyperlinks/LinkOperationHyperlinkDetector.java @@ -14,16 +14,13 @@ import java.util.List; import java.util.Objects; -import org.eclipse.jface.text.IRegion; import org.eclipse.jface.text.ITextViewer; import org.eclipse.jface.text.hyperlink.IHyperlink; import com.fasterxml.jackson.core.JsonPointer; +import com.fasterxml.jackson.databind.JsonNode; import com.reprezen.swagedit.core.editor.JsonDocument; import com.reprezen.swagedit.core.hyperlinks.AbstractJsonHyperlinkDetector; -import com.reprezen.swagedit.core.hyperlinks.SwaggerHyperlink; -import com.reprezen.swagedit.core.model.AbstractNode; -import com.reprezen.swagedit.core.model.Model; public class LinkOperationHyperlinkDetector extends AbstractJsonHyperlinkDetector { @@ -34,26 +31,25 @@ protected boolean canDetect(JsonPointer pointer) { @Override protected IHyperlink[] doDetect(JsonDocument doc, ITextViewer viewer, HyperlinkInfo info, JsonPointer pointer) { - Model model = doc.getModel(); - AbstractNode node = model.find(pointer); - List nodes = model.findByType(JsonPointer.compile("/definitions/operation")); - Iterator it = nodes.iterator(); + JsonNode node = doc.asJson().at(pointer); + List nodes = doc.getContent().findByType(JsonPointer.compile("/definitions/operation")); + Iterator it = nodes.iterator(); - AbstractNode found = null; + JsonNode found = null; while (it.hasNext() && found == null) { - AbstractNode current = it.next(); - AbstractNode value = current.get("operationId"); + JsonNode current = it.next(); + JsonNode value = current.get("operationId"); - if (value != null && Objects.equals(node.asValue().getValue(), value.asValue().getValue())) { + if (value != null && Objects.equals(node.asText(), value.asText())) { found = value; } } if (found != null) { - IRegion target = doc.getRegion(found.getPointer()); - if (target != null) { - return new IHyperlink[] { new SwaggerHyperlink(info.text, viewer, info.region, target) }; - } + // IRegion target = doc.getRegion(found.getPointer()); + // if (target != null) { + // return new IHyperlink[] { new SwaggerHyperlink(info.text, viewer, info.region, target) }; + // } } return null; diff --git a/com.reprezen.swagedit.openapi3/src/com/reprezen/swagedit/openapi3/hyperlinks/SecuritySchemeHyperlinkDetector.java b/com.reprezen.swagedit.openapi3/src/com/reprezen/swagedit/openapi3/hyperlinks/SecuritySchemeHyperlinkDetector.java index 08e339b0..b0441d54 100644 --- a/com.reprezen.swagedit.openapi3/src/com/reprezen/swagedit/openapi3/hyperlinks/SecuritySchemeHyperlinkDetector.java +++ b/com.reprezen.swagedit.openapi3/src/com/reprezen/swagedit/openapi3/hyperlinks/SecuritySchemeHyperlinkDetector.java @@ -13,16 +13,13 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; -import org.eclipse.jface.text.IRegion; import org.eclipse.jface.text.ITextViewer; import org.eclipse.jface.text.hyperlink.IHyperlink; import com.fasterxml.jackson.core.JsonPointer; +import com.fasterxml.jackson.databind.JsonNode; import com.reprezen.swagedit.core.editor.JsonDocument; import com.reprezen.swagedit.core.hyperlinks.AbstractJsonHyperlinkDetector; -import com.reprezen.swagedit.core.hyperlinks.SwaggerHyperlink; -import com.reprezen.swagedit.core.model.AbstractNode; -import com.reprezen.swagedit.core.model.Model; public class SecuritySchemeHyperlinkDetector extends AbstractJsonHyperlinkDetector { @@ -40,14 +37,13 @@ protected IHyperlink[] doDetect(JsonDocument doc, ITextViewer viewer, HyperlinkI String link = matcher.find() ? matcher.group(1) : null; if (link != null) { - Model model = doc.getModel(); - AbstractNode securityScheme = model.find("/components/securitySchemes/" + link); + JsonNode securityScheme = doc.asJson().at("/components/securitySchemes/" + link); if (securityScheme != null) { - IRegion target = doc.getRegion(securityScheme.getPointer()); - if (target != null) { - return new IHyperlink[] { new SwaggerHyperlink(info.text, viewer, info.region, target) }; - } + // IRegion target = doc.getRegion(securityScheme.getPointer()); + // if (target != null) { + // return new IHyperlink[] { new SwaggerHyperlink(info.text, viewer, info.region, target) }; + // } } } return null; diff --git a/com.reprezen.swagedit.openapi3/src/com/reprezen/swagedit/openapi3/templates/OpenApi3ContextTypeProvider.java b/com.reprezen.swagedit.openapi3/src/com/reprezen/swagedit/openapi3/templates/OpenApi3ContextTypeProvider.java index 105424c1..2676acbd 100644 --- a/com.reprezen.swagedit.openapi3/src/com/reprezen/swagedit/openapi3/templates/OpenApi3ContextTypeProvider.java +++ b/com.reprezen.swagedit.openapi3/src/com/reprezen/swagedit/openapi3/templates/OpenApi3ContextTypeProvider.java @@ -17,14 +17,14 @@ import com.google.common.base.Predicate; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; -import com.reprezen.swagedit.core.model.Model; +import com.reprezen.swagedit.core.editor.JsonDocument; import com.reprezen.swagedit.core.templates.SchemaBasedTemplateContextType; public class OpenApi3ContextTypeProvider { private static final String TEMPLATE_ID_PREFIX = "com.reprezen.swagedit.openapi3.templates."; - public TemplateContextType getContextType(final Model model, final String path) { + public TemplateContextType getContextType(final JsonDocument doc, final String path) { if (OpenApi3ContextTypeProvider.RootContextType.isRoot(path)) { return new RootContextType(); } @@ -34,7 +34,7 @@ public TemplateContextType getContextType(final Model model, final String path) @Override public boolean apply(TemplateContextType input) { if (input instanceof SchemaBasedTemplateContextType) { - return ((SchemaBasedTemplateContextType) input).matches(model, path); + return ((SchemaBasedTemplateContextType) input).matches(doc, path); } return false; } diff --git a/com.reprezen.swagedit.openapi3/src/com/reprezen/swagedit/openapi3/validation/OpenApi3ModelValidator.java b/com.reprezen.swagedit.openapi3/src/com/reprezen/swagedit/openapi3/validation/OpenApi3ModelValidator.java new file mode 100644 index 00000000..0bfeac69 --- /dev/null +++ b/com.reprezen.swagedit.openapi3/src/com/reprezen/swagedit/openapi3/validation/OpenApi3ModelValidator.java @@ -0,0 +1,172 @@ +package com.reprezen.swagedit.openapi3.validation; + +import java.util.Iterator; +import java.util.List; +import java.util.Objects; +import java.util.Set; + +import org.eclipse.core.resources.IMarker; + +import com.fasterxml.jackson.core.JsonPointer; +import com.fasterxml.jackson.databind.JsonNode; +import com.google.common.collect.Lists; +import com.reprezen.swagedit.core.json.JsonModel; +import com.reprezen.swagedit.core.json.JsonRegion; +import com.reprezen.swagedit.core.schema.TypeDefinition; +import com.reprezen.swagedit.core.validation.Messages; +import com.reprezen.swagedit.core.validation.ModelValidator; +import com.reprezen.swagedit.core.validation.SwaggerError; + +public class OpenApi3ModelValidator extends ModelValidator { + + private final JsonPointer operationPointer = JsonPointer.compile("/definitions/operation"); + private final JsonPointer securityPointer = JsonPointer.compile("/components/securitySchemes"); + private final List oauthScopes = Lists.newArrayList("oauth2", "openIdConnect"); + + public OpenApi3ModelValidator(JsonModel model) { + super(model); + } + + @Override + protected void executeModelValidation(JsonNode node, JsonRegion region, Set errors) { + super.executeModelValidation(node, region, errors); + validateSecuritySchemeReferences(node, region, errors); + validateOperationIdReferences(node, region, errors); + validateParameters(node, region, errors); + } + + void validateOperationIdReferences(JsonNode node, JsonRegion region, Set errors) { + final JsonPointer schemaPointer = JsonPointer.compile("/definitions/link/properties/operationId"); + final TypeDefinition definition = model.getTypes().get(region.pointer); + + if (node != null && definition != null && schemaPointer.equals(definition.getPointer())) { + List nodes = model.findByType(operationPointer); + Iterator it = nodes.iterator(); + + boolean found = false; + while (it.hasNext() && !found) { + JsonNode current = it.next(); + JsonNode value = current.get("operationId"); + + found = value != null && Objects.equals(node.asText(), value.asText()); + } + + if (!found) { + errors.add(error(region, IMarker.SEVERITY_ERROR, Messages.error_invalid_operation_id)); + } + } + } + + void validateParameters(JsonNode node, JsonRegion region, Set errors) { + final JsonPointer pointer = JsonPointer.compile("/definitions/parameterOrReference"); + final TypeDefinition definition = model.getTypes().get(region.pointer); + + if (node != null && definition != null && pointer.equals(definition.getPointer())) { + // validation parameter location value + if (node.isObject() && node.has("in")) { + JsonNode valueNode = node.get("in"); + try { + Object value = valueNode.asText(); + + if (!Lists.newArrayList("query", "header", "path", "cookie").contains(value)) { + errors.add(error(getRegion(region, "in"), IMarker.SEVERITY_ERROR, + Messages.error_invalid_parameter_location)); + } + } catch (Exception e) { + errors.add(error(getRegion(region, "in"), IMarker.SEVERITY_ERROR, + Messages.error_invalid_parameter_location)); + } + } + } + } + + void validateSecuritySchemeReferences(JsonNode node, JsonRegion region, Set errors) { + if (region.pointer.toString().matches(".*/security/\\d+")) { + JsonNode securitySchemes = model.getContent().at(securityPointer); + + if (node.isObject()) { + for (Iterator it = node.fieldNames(); it.hasNext();) { + String field = it.next(); + JsonNode securityScheme = securitySchemes.get(field); + + if (securityScheme == null) { + String message = Messages.error_invalid_reference_type + + " It should be a valid security scheme."; + + errors.add(error(getRegion(region, field), IMarker.SEVERITY_ERROR, message)); + } else { + validateSecuritySchemeScopes(node, region, field, securityScheme, errors); + } + } + } + } + } + + + private void validateSecuritySchemeScopes(JsonNode node, JsonRegion region, String name, JsonNode securityScheme, + Set errors) { + String type = getType(securityScheme); + if (type == null) { + return; + } + + boolean shouldHaveScopes = oauthScopes.contains(type); + List scopes = getSecurityScopes(securityScheme); + + JsonNode values = node.get(name); + if (values.isArray()) { + if (values.size() > 0 && !shouldHaveScopes) { + String message = String.format(Messages.error_scope_should_be_empty, name, type, name); + + errors.add(error(getRegion(region, name), IMarker.SEVERITY_ERROR, message)); + } else if (values.size() == 0 && shouldHaveScopes) { + String message = String.format(Messages.error_scope_should_not_be_empty, name, type); + + errors.add(error(getRegion(region, name), IMarker.SEVERITY_ERROR, message)); + } else { + for (int i = 0; i < values.size(); i++) { + JsonNode scope = values.get(i); + try { + String scopeName = scope.asText(); + if (!scopes.contains(scopeName)) { + String message = String.format(Messages.error_invalid_scope_reference, scopeName, name); + errors.add( + error(getRegion(region, "/name/" + i + "/scope"), IMarker.SEVERITY_ERROR, message)); + } + } catch (Exception e) { + // Invalid scope name type. + // No need to create an error, it will be handle by the schema validation. + } + } + } + } + } + + private String getType(JsonNode securityScheme) { + JsonNode type = securityScheme.get("type"); + if (type == null) { + return null; + } + + return type.asText(); + } + + private List getSecurityScopes(JsonNode securityScheme) { + List scopes = Lists.newArrayList(); + + try { + JsonNode flows = securityScheme.get("flows"); + for (Iterator it = flows.elements(); it.hasNext();) { + JsonNode flow = it.next(); + JsonNode values = flow.get("scopes"); + if (values != null && values.isObject()) { + scopes.addAll(Lists.newArrayList(values.fieldNames())); + } + } + } catch (Exception e) { + // could be a NPE, let's just return the scopes we have so far. + } + return scopes; + } + +} diff --git a/com.reprezen.swagedit.openapi3/src/com/reprezen/swagedit/openapi3/validation/OpenApi3ReferenceValidator.java b/com.reprezen.swagedit.openapi3/src/com/reprezen/swagedit/openapi3/validation/OpenApi3ReferenceValidator.java index fa61ca89..247dc48d 100644 --- a/com.reprezen.swagedit.openapi3/src/com/reprezen/swagedit/openapi3/validation/OpenApi3ReferenceValidator.java +++ b/com.reprezen.swagedit.openapi3/src/com/reprezen/swagedit/openapi3/validation/OpenApi3ReferenceValidator.java @@ -11,24 +11,14 @@ package com.reprezen.swagedit.openapi3.validation; import java.net.URI; -import java.util.Objects; import java.util.Set; -import org.eclipse.core.resources.IMarker; - import com.fasterxml.jackson.core.JsonPointer; import com.fasterxml.jackson.databind.JsonNode; -import com.github.fge.jsonschema.core.exceptions.ProcessingException; -import com.github.fge.jsonschema.core.report.ProcessingReport; -import com.github.fge.jsonschema.main.JsonSchema; import com.reprezen.swagedit.core.editor.JsonDocument; import com.reprezen.swagedit.core.json.references.JsonReference; import com.reprezen.swagedit.core.json.references.JsonReferenceFactory; import com.reprezen.swagedit.core.json.references.JsonReferenceValidator; -import com.reprezen.swagedit.core.model.AbstractNode; -import com.reprezen.swagedit.core.model.ValueNode; -import com.reprezen.swagedit.core.schema.TypeDefinition; -import com.reprezen.swagedit.core.validation.Messages; import com.reprezen.swagedit.core.validation.SwaggerError; public class OpenApi3ReferenceValidator extends JsonReferenceValidator { @@ -45,55 +35,55 @@ public OpenApi3ReferenceValidator() { } @Override - protected void validateType(JsonDocument doc, URI baseURI, AbstractNode node, JsonReference reference, + protected void validateType(JsonDocument doc, URI baseURI, JsonNode node, JsonReference reference, Set errors) { - if (linkTypePointer.equals(node.getType().getPointer())) { - JsonNode target = findTarget(doc, baseURI, reference); - - if (factory != null) { - JsonSchema jsonSchema; - try { - jsonSchema = factory.getJsonSchema(doc.getSchema().asJson(), operationTypePointer.toString()); - ProcessingReport report = jsonSchema.validate(target); - if (!report.isSuccess()) { - errors.add(createReferenceError(IMarker.SEVERITY_WARNING, Messages.error_invalid_operation_ref, - reference)); - } - } catch (ProcessingException e) { - e.printStackTrace(); - } - } - } else { + // if (linkTypePointer.equals(node.getType().getPointer())) { + // JsonNode target = findTarget(doc, baseURI, reference); + // + // if (factory != null) { + // JsonSchema jsonSchema; + // try { + // jsonSchema = factory.getJsonSchema(doc.getSchema().asJson(), operationTypePointer.toString()); + // ProcessingReport report = jsonSchema.validate(target); + // if (!report.isSuccess()) { + // errors.add(createReferenceError(IMarker.SEVERITY_WARNING, Messages.error_invalid_operation_ref, + // reference)); + // } + // } catch (ProcessingException e) { + // e.printStackTrace(); + // } + // } + // } else { super.validateType(doc, baseURI, node, reference, errors); - } + // } } - protected boolean isValidOperation(AbstractNode operation) { - TypeDefinition type = operation != null ? operation.getType() : null; - - return type != null && Objects.equals(operationTypePointer, type.getPointer()); - } + // protected boolean isValidOperation(JsonNode operation) { + // TypeDefinition type = operation != null ? operation.getType() : null; + // + // return type != null && Objects.equals(operationTypePointer, type.getPointer()); + // } public static class OpenApi3ReferenceFactory extends JsonReferenceFactory { private static final String OPERATION_REF = "operationRef"; @Override - protected Boolean isReference(AbstractNode node) { + protected Boolean isReference(JsonNode node) { return super.isReference(node) || node.get(OPERATION_REF) != null; } - @Override - protected ValueNode getReferenceValue(AbstractNode node) { - ValueNode valueNode = super.getReferenceValue(node); - if (valueNode == null) { - AbstractNode other = node.get(OPERATION_REF); - if (other != null && other.isValue()) { - valueNode = other.asValue(); - } - } - return valueNode; - } + // @Override + // protected JsonNode getReferenceValue(JsonNode node) { + // JsonNode valueNode = super.getReferenceValue(node); + // if (valueNode == null) { + // AbstractNode other = node.get(OPERATION_REF); + // if (other != null && other.isValue()) { + // valueNode = other.asValue(); + // } + // } + // return valueNode; + // } } } \ No newline at end of file diff --git a/com.reprezen.swagedit.openapi3/src/com/reprezen/swagedit/openapi3/validation/OpenApi3Validator.java b/com.reprezen.swagedit.openapi3/src/com/reprezen/swagedit/openapi3/validation/OpenApi3Validator.java index aa3cbb23..1716b205 100644 --- a/com.reprezen.swagedit.openapi3/src/com/reprezen/swagedit/openapi3/validation/OpenApi3Validator.java +++ b/com.reprezen.swagedit.openapi3/src/com/reprezen/swagedit/openapi3/validation/OpenApi3Validator.java @@ -10,172 +10,27 @@ *******************************************************************************/ package com.reprezen.swagedit.openapi3.validation; -import java.util.Iterator; -import java.util.List; import java.util.Map; -import java.util.Objects; -import java.util.Set; -import org.eclipse.core.resources.IMarker; - -import com.fasterxml.jackson.core.JsonPointer; import com.fasterxml.jackson.databind.JsonNode; -import com.google.common.collect.Lists; -import com.reprezen.swagedit.core.model.AbstractNode; -import com.reprezen.swagedit.core.model.ArrayNode; -import com.reprezen.swagedit.core.model.Model; -import com.reprezen.swagedit.core.validation.Messages; -import com.reprezen.swagedit.core.validation.SwaggerError; +import com.reprezen.swagedit.core.json.JsonModel; +import com.reprezen.swagedit.core.json.references.JsonReferenceValidator; +import com.reprezen.swagedit.core.validation.ModelValidator; import com.reprezen.swagedit.core.validation.Validator; public class OpenApi3Validator extends Validator { - private final JsonPointer operationPointer = JsonPointer.compile("/definitions/operation"); - private final JsonPointer securityPointer = JsonPointer.compile("/components/securitySchemes"); - public OpenApi3Validator(Map preloadedSchemas) { super(new OpenApi3ReferenceValidator(), preloadedSchemas); } - OpenApi3Validator(OpenApi3ReferenceValidator validator, Map preloadedSchemas) { - super(validator, preloadedSchemas); + OpenApi3Validator(JsonReferenceValidator referenceValidator, Map preloadedSchemas) { + super(referenceValidator, preloadedSchemas); } @Override - protected void executeModelValidation(Model model, AbstractNode node, Set errors) { - super.executeModelValidation(model, node, errors); - validateOperationIdReferences(model, node, errors); - validateSecuritySchemeReferences(model, node, errors); - validateParameters(model, node, errors); - } - - private void validateSecuritySchemeReferences(Model model, AbstractNode node, Set errors) { - if (node.getPointerString().matches(".*/security/\\d+")) { - AbstractNode securitySchemes = model.find(securityPointer); - - if (node.isObject()) { - for (String field : node.asObject().fieldNames()) { - AbstractNode securityScheme = securitySchemes.get(field); - - if (securityScheme == null) { - String message = Messages.error_invalid_reference_type - + " It should be a valid security scheme."; - - errors.add(error(node.get(field), IMarker.SEVERITY_ERROR, message)); - } else { - validateSecuritySchemeScopes(node, field, securityScheme, errors); - } - } - } - } - } - - private List oauthScopes = Lists.newArrayList("oauth2", "openIdConnect"); - - private void validateSecuritySchemeScopes(AbstractNode node, String name, AbstractNode securityScheme, - Set errors) { - String type = getType(securityScheme); - if (type == null) { - return; - } - - boolean shouldHaveScopes = oauthScopes.contains(type); - List scopes = getSecurityScopes(securityScheme); - - AbstractNode values = node.get(name); - if (values.isArray()) { - ArrayNode scopeValues = values.asArray(); - - if (scopeValues.size() > 0 && !shouldHaveScopes) { - String message = String.format(Messages.error_scope_should_be_empty, name, type, name); - - errors.add(error(node.get(name), IMarker.SEVERITY_ERROR, message)); - } else if (scopeValues.size() == 0 && shouldHaveScopes) { - String message = String.format(Messages.error_scope_should_not_be_empty, name, type); - - errors.add(error(node.get(name), IMarker.SEVERITY_ERROR, message)); - } else { - for (AbstractNode scope : scopeValues.elements()) { - try { - String scopeName = (String) scope.asValue().getValue(); - if (!scopes.contains(scopeName)) { - String message = String.format(Messages.error_invalid_scope_reference, scopeName, name); - - errors.add(error(scope, IMarker.SEVERITY_ERROR, message)); - } - } catch (Exception e) { - // Invalid scope name type. - // No need to create an error, it will be handle by the schema validation. - } - } - } - } + public ModelValidator getModelValidator(JsonModel model) { + return new OpenApi3ModelValidator(model); } - private String getType(AbstractNode securityScheme) { - AbstractNode type = securityScheme.get("type"); - if (type == null) { - return null; - } - - return (String) type.asValue().getValue(); - } - - private List getSecurityScopes(AbstractNode securityScheme) { - List scopes = Lists.newArrayList(); - - try { - AbstractNode flows = securityScheme.get("flows"); - for (AbstractNode flow : flows.elements()) { - AbstractNode values = flow.get("scopes"); - if (values != null && values.isObject()) { - scopes.addAll(values.asObject().fieldNames()); - } - } - } catch (Exception e) { - // could be a NPE, let's just return the scopes we have so far. - } - return scopes; - } - - protected void validateOperationIdReferences(Model model, AbstractNode node, Set errors) { - JsonPointer schemaPointer = JsonPointer.compile("/definitions/link/properties/operationId"); - - if (node != null && node.getType() != null && schemaPointer.equals(node.getType().getPointer())) { - List nodes = model.findByType(operationPointer); - Iterator it = nodes.iterator(); - - boolean found = false; - while (it.hasNext() && !found) { - AbstractNode current = it.next(); - AbstractNode value = current.get("operationId"); - - found = value != null && Objects.equals(node.asValue().getValue(), value.asValue().getValue()); - } - - if (!found) { - errors.add(error(node, IMarker.SEVERITY_ERROR, Messages.error_invalid_operation_id)); - } - } - } - - protected void validateParameters(Model model, AbstractNode node, Set errors) { - final JsonPointer pointer = JsonPointer.compile("/definitions/parameterOrReference"); - - if (node != null && node.getType() != null && pointer.equals(node.getType().getPointer())) { - // validation parameter location value - if (node.isObject() && node.asObject().get("in") != null) { - AbstractNode valueNode = node.asObject().get("in"); - try { - Object value = valueNode.asValue().getValue(); - - if (!Lists.newArrayList("query", "header", "path", "cookie").contains(value)) { - errors.add(error(valueNode, IMarker.SEVERITY_ERROR, Messages.error_invalid_parameter_location)); - } - } catch (Exception e) { - errors.add(error(valueNode, IMarker.SEVERITY_ERROR, Messages.error_invalid_parameter_location)); - } - } - } - } } diff --git a/com.reprezen.swagedit.tests/src/com/reprezen/swagedit/assist/JsonReferenceProposalProviderTest.xtend b/com.reprezen.swagedit.tests/src/com/reprezen/swagedit/assist/JsonReferenceProposalProviderTest.xtend index 513b5579..a314334c 100644 --- a/com.reprezen.swagedit.tests/src/com/reprezen/swagedit/assist/JsonReferenceProposalProviderTest.xtend +++ b/com.reprezen.swagedit.tests/src/com/reprezen/swagedit/assist/JsonReferenceProposalProviderTest.xtend @@ -59,7 +59,7 @@ class JsonReferenceProposalProviderTest { val document = new SwaggerDocument document.set(text) - val proposals = provider.getProposals("/paths/~1foo/get/responses/200/schema/$ref".ptr, document, Scope.LOCAL) + val proposals = provider.getProposals("/paths/~1foo/get/responses/200/schema/$ref".ptr, document.content, Scope.LOCAL) assertThat(proposals, hasItems( new Proposal("\"#/definitions/Valid\"", "Valid", null, "#/definitions/Valid") @@ -86,7 +86,7 @@ class JsonReferenceProposalProviderTest { val document = new SwaggerDocument document.set(text) - val proposals = provider.getProposals("/definitions/Bar/properties/foo/$ref".ptr, document, Scope.LOCAL) + val proposals = provider.getProposals("/definitions/Bar/properties/foo/$ref".ptr, document.content, Scope.LOCAL) assertThat(proposals, hasItems( new Proposal("\"#/definitions/Foo\"", "Foo", null, "#/definitions/Foo") diff --git a/com.reprezen.swagedit.tests/src/com/reprezen/swagedit/assist/SwaggerContentAssistProcessorTest.xtend b/com.reprezen.swagedit.tests/src/com/reprezen/swagedit/assist/SwaggerContentAssistProcessorTest.xtend index 10db0aac..53c4874c 100644 --- a/com.reprezen.swagedit.tests/src/com/reprezen/swagedit/assist/SwaggerContentAssistProcessorTest.xtend +++ b/com.reprezen.swagedit.tests/src/com/reprezen/swagedit/assist/SwaggerContentAssistProcessorTest.xtend @@ -1,5 +1,7 @@ package com.reprezen.swagedit.assist +import com.reprezen.swagedit.core.assist.StyledCompletionProposal +import com.reprezen.swagedit.core.editor.JsonDocument import com.reprezen.swagedit.editor.SwaggerDocument import java.util.ArrayList import org.junit.Test @@ -7,13 +9,12 @@ import org.junit.Test import static com.reprezen.swagedit.tests.utils.Cursors.* import static org.hamcrest.core.IsCollectionContaining.* import static org.junit.Assert.* -import com.reprezen.swagedit.core.assist.StyledCompletionProposal -import com.reprezen.swagedit.core.model.Model +import com.reprezen.swagedit.core.json.JsonModel class SwaggerContentAssistProcessorTest { val processor = new SwaggerContentAssistProcessor(null) { - override protected initTextMessages(Model model) { + override protected initTextMessages(JsonModel doc) { new ArrayList } @@ -24,6 +25,10 @@ class SwaggerContentAssistProcessorTest { override protected getTemplateStore() { null } + + override protected getContextTypeId(JsonDocument doc, String path) { + throw new UnsupportedOperationException("TODO: auto-generated method stub") + } } @Test @@ -83,13 +88,12 @@ class SwaggerContentAssistProcessorTest { <1> ''', document) - val proposals = test.apply(processor, "1") - assertThat(proposals.map[(it as StyledCompletionProposal).replacementString], - hasItems( - "name:", - "url:", - "x-:" - )) + val proposals = test.apply(processor, "1") + assertThat(proposals.map[(it as StyledCompletionProposal).replacementString], hasItems( + "name:", + "url:", + "x-:" + )) } @Test @@ -114,155 +118,94 @@ class SwaggerContentAssistProcessorTest { ''', document) var proposals = test.apply(processor, "1") - assertThat(proposals.map[(it as StyledCompletionProposal).replacementString], - hasItems( - "-" - )) + assertThat(proposals.map[(it as StyledCompletionProposal).replacementString], hasItems( + "-" + )) proposals = test.apply(processor, "2") - assertThat(proposals.map[(it as StyledCompletionProposal).replacementString], - hasItems( - "" - )) + assertThat(proposals.map[(it as StyledCompletionProposal).replacementString], hasItems( + "" + )) proposals = test.apply(processor, "3") - assertThat(proposals.map[(it as StyledCompletionProposal).replacementString], - hasItems( - "uniqueItems:", - "format:", - "maxItems:", - "$ref:", - "schema:", - "maximum:", - "required:", - "collectionFormat:", - "allowEmptyValue:", - "minLength:", - "maxLength:" - )) - - proposals = test.apply(processor, "4") - assertThat(proposals.map[(it as StyledCompletionProposal).replacementString], - hasItems( - "body", - "query", - "path", - "header", - "formData" - )) + assertThat(proposals.map[(it as StyledCompletionProposal).replacementString], hasItems( + "uniqueItems:", + "format:", + "maxItems:", + "$ref:", + "schema:", + "maximum:", + "required:", + "collectionFormat:", + "allowEmptyValue:", + "minLength:", + "maxLength:" + )) - proposals = test.apply(processor, "5") - assertThat(proposals.map[(it as StyledCompletionProposal).replacementString], - hasItems( - "uniqueItems:", - "format:", - "maxItems:", - "$ref:", - "schema:", - "maximum:", - "required:", - "collectionFormat:", - "allowEmptyValue:", - "minLength:", - "maxLength:" - )) + proposals = test.apply(processor, "4") + assertThat(proposals.map[(it as StyledCompletionProposal).replacementString], hasItems( + "body", + "query", + "path", + "header", + "formData" + )) + + proposals = test.apply(processor, "5") + assertThat(proposals.map[(it as StyledCompletionProposal).replacementString], hasItems( + "uniqueItems:", + "format:", + "maxItems:", + "$ref:", + "schema:", + "maximum:", + "required:", + "collectionFormat:", + "allowEmptyValue:", + "minLength:", + "maxLength:" + )) } @Test def void test3() { val test = setUpContentAssistTest(''' - definitions: - Foo: - <1> - Product: - <2>required: - - name - properties: - name: - type: string - description: - type: <3> + definitions: + Foo: + <1> + Product: + <2>required: + - name + properties: + name: + type: string + description: + type: <3> ''', new SwaggerDocument) var proposals = test.apply(processor, "1") var values = proposals.map[(it as StyledCompletionProposal).replacementString] assertEquals(30, values.size) - assertThat(values, hasItems( - "$ref:", - "format:", - "title:", - "description:", - "multipleOf:", - "maximum:", - "exclusiveMaximum:", - "minimum:", - "exclusiveMinimum:", - "maxLength:", - "minLength:", - "pattern:", - "maxItems:", - "minItems:", - "uniqueItems:", - "maxProperties:", - "minProperties:", - "required:", - "enum:", - "additionalProperties:", - "type:", - "items:", - "allOf:", - "properties:", - "discriminator:", - "readOnly:", - "xml:", - "externalDocs:", - "example:", - "x-:")) + assertThat(values, + hasItems("$ref:", "format:", "title:", "description:", "multipleOf:", "maximum:", "exclusiveMaximum:", + "minimum:", "exclusiveMinimum:", "maxLength:", "minLength:", "pattern:", "maxItems:", "minItems:", + "uniqueItems:", "maxProperties:", "minProperties:", "required:", "enum:", "additionalProperties:", + "type:", "items:", "allOf:", "properties:", "discriminator:", "readOnly:", "xml:", "externalDocs:", + "example:", "x-:")) proposals = test.apply(processor, "2") values = proposals.map[(it as StyledCompletionProposal).replacementString] // same without required and properties assertEquals(28, values.size) - assertThat(values, hasItems( - "$ref:", - "format:", - "title:", - "description:", - "multipleOf:", - "maximum:", - "exclusiveMaximum:", - "minimum:", - "exclusiveMinimum:", - "maxLength:", - "minLength:", - "pattern:", - "maxItems:", - "minItems:", - "uniqueItems:", - "maxProperties:", - "minProperties:", - "enum:", - "additionalProperties:", - "type:", - "items:", - "allOf:", - "discriminator:", - "readOnly:", - "xml:", - "externalDocs:", - "example:", - "x-:")) - + assertThat(values, + hasItems("$ref:", "format:", "title:", "description:", "multipleOf:", "maximum:", "exclusiveMaximum:", + "minimum:", "exclusiveMinimum:", "maxLength:", "minLength:", "pattern:", "maxItems:", "minItems:", + "uniqueItems:", "maxProperties:", "minProperties:", "enum:", "additionalProperties:", "type:", "items:", + "allOf:", "discriminator:", "readOnly:", "xml:", "externalDocs:", "example:", "x-:")) + proposals = test.apply(processor, "3") values = proposals.map[(it as StyledCompletionProposal).replacementString] assertEquals(7, values.size) - assertThat(values, hasItems( - "array", - "boolean", - "integer", - "\"null\"", - "number", - "object", - "string")) + assertThat(values, hasItems("array", "boolean", "integer", "\"null\"", "number", "object", "string")) } } diff --git a/com.reprezen.swagedit.tests/src/com/reprezen/swagedit/assist/SwaggerProposalProviderTest.xtend b/com.reprezen.swagedit.tests/src/com/reprezen/swagedit/assist/SwaggerProposalProviderTest.xtend index 18bb4fd4..2b957aaa 100644 --- a/com.reprezen.swagedit.tests/src/com/reprezen/swagedit/assist/SwaggerProposalProviderTest.xtend +++ b/com.reprezen.swagedit.tests/src/com/reprezen/swagedit/assist/SwaggerProposalProviderTest.xtend @@ -1,37 +1,29 @@ package com.reprezen.swagedit.assist -import com.reprezen.swagedit.core.model.Model import com.reprezen.swagedit.core.schema.JsonType import com.reprezen.swagedit.core.schema.MultipleTypeDefinition import com.reprezen.swagedit.tests.utils.PointerHelpers import org.hamcrest.Matcher -import org.junit.Before import org.junit.Test import static org.hamcrest.core.IsCollectionContaining.* import static org.junit.Assert.* import com.reprezen.swagedit.schema.SwaggerSchema import com.reprezen.swagedit.core.assist.JsonProposalProvider +import com.reprezen.swagedit.core.json.JsonModel class SwaggerProposalProviderTest { extension PointerHelpers = new PointerHelpers - val schema = new SwaggerSchema + val schema = new SwaggerSchema val provider = new JsonProposalProvider - var Model model - - @Before - def void setUp() { - model = Model.empty(schema) - } @Test def void testGetProposals_RootObject() { - val node = model.objectNode(null, "".ptr) - node.type = schema.getType(node) + val model = new JsonModel(schema, "", false) - assertThat(provider.getProposals(node).map[replacementString], hasItems( + assertThat(provider.getProposals("".ptr, model, "").map[replacementString], hasItems( "swagger:", "info:", "host:", @@ -53,20 +45,18 @@ class SwaggerProposalProviderTest { @Test def void testGetProposals_SwaggerEnum() { - val node = model.valueNode(null, "/swagger".ptr, null) - node.type = schema.getType(node) + val model = new JsonModel(schema, "swagger: ", false) - assertThat(provider.getProposals(node).map [ + assertThat(provider.getProposals("/swagger".ptr, model, "").map [ replacementString ], hasItems("\"2.0\"")) } @Test def void testGetProposals_InfoObject() { - val node = model.objectNode(null, "/info".ptr) - node.type = schema.getType(node) + val model = new JsonModel(schema, "info: ", false) - assertThat(provider.getProposals(node).map [ + assertThat(provider.getProposals("/info".ptr, model, "").map [ replacementString ], hasItems( "title:", @@ -80,10 +70,9 @@ class SwaggerProposalProviderTest { @Test def void testGetProposals_SchemesArray() { - val node = model.arrayNode(null, "/schemes".ptr) - node.type = schema.getType(node) + val model = new JsonModel(schema, "schemes: ", false) - assertThat(provider.getProposals(node).map [ + assertThat(provider.getProposals("/schemes".ptr, model, "").map [ replacementString ], hasItems( "- http", @@ -95,10 +84,9 @@ class SwaggerProposalProviderTest { @Test def void testGetProposals_PathsObject() { - val node = model.objectNode(null, "/paths".ptr) - node.type = schema.getType(node) + val model = new JsonModel(schema, "paths: ", false) - assertThat(provider.getProposals(node).map [ + assertThat(provider.getProposals("/paths".ptr, model, "").map [ replacementString ], hasItems( "/:", @@ -108,34 +96,39 @@ class SwaggerProposalProviderTest { @Test def void testGetProposals_DefinitionsObject() { - val node = model.objectNode(null, "/definitions".ptr) - node.type = schema.getType(node) + val model = new JsonModel(schema, "definitions: ", false) - assertThat(provider.getProposals(node).map [ + assertThat(provider.getProposals("/definitions".ptr, model, "").map [ replacementString ], hasItems( "(schema name):" )) } - + @Test def void testGetProposals_SchemaPropertiesObject() { - val node = model.objectNode(null, "/definitions/MyType/properties".ptr) - node.type = schema.getType(node) + val model = new JsonModel(schema, ''' + definitions: + MyType: + properties: + ''', false) - assertThat(provider.getProposals(node).map [ + assertThat(provider.getProposals("/definitions/MyType/properties".ptr, model, "").map [ replacementString ], hasItems( "(property name):" )) - } + } @Test def void testPathGetProposals() { - val node = model.objectNode(null, "/paths/~1/get".ptr) - node.type = schema.getType(node) + val model = new JsonModel(schema, ''' + paths: + /: + get: + ''', false) - assertThat(provider.getProposals(node).map [ + assertThat(provider.getProposals("/paths/~1/get".ptr, model, "").map [ replacementString ], hasItems( "tags:", @@ -156,10 +149,14 @@ class SwaggerProposalProviderTest { @Test def void testPathParametersProposals() { - val node = model.arrayNode(null, "/paths/~1/get/parameters".ptr) - node.type = schema.getType(node) - - assertThat(provider.getProposals(node).map [ + val model = new JsonModel(schema, ''' + paths: + /: + get: + parameters: + ''', false) + + assertThat(provider.getProposals("/paths/~1/get/parameters".ptr, model, "").map [ replacementString ], hasItems( "-" @@ -168,10 +165,15 @@ class SwaggerProposalProviderTest { @Test def void testParameterInProposals() { - val node = model.valueNode(null, "/paths/~1/get/parameters/0/in".ptr, null) - node.type = schema.getType(node) - - assertThat(provider.getProposals(node).map [ + val model = new JsonModel(schema, ''' + paths: + /: + get: + parameters: + - in: + ''', false) + + assertThat(provider.getProposals("/paths/~1/get/parameters/0/in".ptr, model, "").map [ replacementString ], hasItems( "header", @@ -184,10 +186,15 @@ class SwaggerProposalProviderTest { @Test def void testGetOneOfProposals() { - val node = model.objectNode(null, "/paths/~1/get/responses/200".ptr) - node.type = schema.getType(node) - - assertThat(provider.getProposals(node).map [ + val model = new JsonModel(schema, ''' + paths: + /: + get: + responses: + 200: + ''', false) + + assertThat(provider.getProposals("/paths/~1/get/responses/200".ptr, model, "").map [ replacementString ], hasItems( "description:", @@ -201,12 +208,17 @@ class SwaggerProposalProviderTest { @Test def void testGetAnyOfProposals() { - val node = model.valueNode(null, "/paths/~1/get/parameters/0/format".ptr, null) - node.type = schema.getType(node) + val model = new JsonModel(schema, ''' + paths: + /: + get: + parameters: + - format: + ''', false) - assertTrue(node.type instanceof MultipleTypeDefinition) + assertTrue(model.getType("/paths/~1/get/parameters/0/format".ptr) instanceof MultipleTypeDefinition) - assertThat(provider.getProposals(node).map [ + assertThat(provider.getProposals("/paths/~1/get/parameters/0/format".ptr, model, "").map [ replacementString ], hasItems( "", @@ -224,12 +236,14 @@ class SwaggerProposalProviderTest { @Test def void testGetParameterRequired() { - val node = model.objectNode(null, "/parameters/foo".ptr) - node.type = schema.getType(node) + val model = new JsonModel(schema, ''' + parameters: + foo: + ''', false) - assertEquals(JsonType.ONE_OF, node.type.type) + assertEquals(JsonType.ONE_OF, model.getType("/parameters/foo".ptr).type) - val values = provider.getProposals(node).map [ + val values = provider.getProposals("/parameters/foo".ptr, model, "").map [ replacementString ] @@ -238,10 +252,17 @@ class SwaggerProposalProviderTest { @Test def void testGetResponsesType() { - val node = model.objectNode(null, "/paths/~1foo/get/responses/200/schema/type".ptr) - node.type = schema.getType(node) - - val values = provider.getProposals(node).map [ + val model = new JsonModel(schema, ''' + paths: + /foo: + get: + responses: + 200: + schema: + type: + ''', false) + + val values = provider.getProposals("/paths/~1foo/get/responses/200/schema/type".ptr, model, "").map [ replacementString ] diff --git a/com.reprezen.swagedit.tests/src/com/reprezen/swagedit/editor/outline/AbstractNodeTest.xtend b/com.reprezen.swagedit.tests/src/com/reprezen/swagedit/editor/outline/AbstractNodeTest.xtend deleted file mode 100644 index 9d624daa..00000000 --- a/com.reprezen.swagedit.tests/src/com/reprezen/swagedit/editor/outline/AbstractNodeTest.xtend +++ /dev/null @@ -1,379 +0,0 @@ -package com.reprezen.swagedit.editor.outline - -import com.reprezen.swagedit.editor.SwaggerDocument -import com.reprezen.swagedit.core.model.Model -import com.reprezen.swagedit.tests.utils.PointerHelpers -import org.junit.Test - -import static org.junit.Assert.* -import com.reprezen.swagedit.schema.SwaggerSchema - -class AbstractNodeTest { - - extension PointerHelpers = new PointerHelpers - val schema = new SwaggerSchema - - @Test - def void testParsing() { - val text = ''' - info: - title: b - version: 1.0.0 - ''' - - val doc = new SwaggerDocument - doc.set(text) - - val model = Model.parseYaml(schema, text) - val root = model.root - - assertTrue(root.isObject) - assertNull(root.parent) - assertNotNull(root.get("info")) - - val info = root.get("info") - assertTrue(info.isObject) - assertSame(root, info.parent) - - assertNotNull(info.get("title")) - assertNotNull(info.get("version")) - - val title = info.get("title") - assertFalse(title.object) - assertFalse(title.array) - assertEquals("b", title.asValue.getValue) - - val version = info.get("version") - assertFalse(version.object) - assertFalse(version.array) - assertEquals("1.0.0", version.asValue.getValue) - - assertSame(info, model.find("/info".ptr)); - assertSame(title, model.find("/info/title".ptr)); - assertSame(version, model.find("/info/version".ptr)); - } - - @Test - def void testCreateFromSingleMapping() { - val text = ''' - foo: bar - ''' - - val model = Model.parseYaml(schema, text) - val el = model.root.get(0) - - assertEquals("foo: bar", el.text) - assertEquals(0, el.elements().size) - - val doc = new SwaggerDocument - doc.set(text) - - val position = el.getPosition(doc) - // end of first line - assertEquals(0, position.offset) - assertEquals(9, position.length) - - // position is first line - assertEquals(0, doc.getLineOfOffset(position.offset)) - } - - @Test - def void testGetType_Enum() { - val text = ''' - swagger: 2.0 - ''' - - val model = Model.parseYaml(schema, text) - assertNotNull(model.root) - - val root = model.root - - assertEquals("".ptr, root.pointer) - assertNotNull(root.get("swagger")) - assertEquals("swagger", root.get("swagger").property) - assertEquals("/swagger".ptr, root.get("swagger").pointer) - } - - @Test - def void testGetType_Object() { - val text = ''' - info: - version: 1.0.0 - title: hello - ''' - - val model = Model.parseYaml(schema, text) - assertNotNull(model.root) - - val root = model.root - - assertEquals("".ptr, root.pointer) - - assertNotNull(root.get("info")) - - val info = root.get("info") - assertEquals("info", info.property) - assertEquals("/info".ptr, info.pointer) - - assertNotNull(info.get("version")) - assertNotNull(info.get("title")) - - val version = info.get("version") - assertEquals("version", version.property) - assertEquals("/info/version".ptr, version.pointer) - - val title = info.get("title") - assertEquals("title", title.property) - assertEquals("/info/title".ptr, title.pointer) - } - - @Test - def void testGetType_StringArray() { - val text = ''' - schemes: - - http - - https - ''' - - val model = Model.parseYaml(schema, text) - assertNotNull(model.root) - - val root = model.root - - assertNotNull(root.get("schemes")) - - val schemes = root.get("schemes") - assertTrue(schemes.isArray) - assertEquals("schemes", schemes.property) - assertEquals("/schemes".ptr, schemes.pointer) - assertEquals(2, schemes.elements.size) - - val first = schemes.elements.get(0) - assertEquals("/schemes/0".ptr, first.pointer) - - val second = schemes.elements.get(1) - assertEquals("/schemes/1".ptr, second.pointer) - } - - @Test - def void testCreateFromArrayValues() { - val text = ''' - foo: - - hello - - world - ''' - - val model = Model.parseYaml(schema, text) - val el = model.root.get(0) - - assertEquals("foo", el.text) - assertEquals(2, el.elements.size) - - assertEquals("hello", el.elements.get(0).text) - assertEquals("world", el.elements.get(1).text) - - val doc = new SwaggerDocument - doc.set(text) - - val position = el.getPosition(doc) - // after foo: - assertEquals(0, position.offset) - assertEquals(5, position.length) - - // should be first line - assertEquals(0, doc.getLineOfOffset(position.offset)) - } - - @Test - def void testCreateFromObjectValues() { - val text = ''' - foo: - k1: hello - k2: world - ''' - - val model = Model.parseYaml(schema, text) - val el = model.root.get(0) - - assertEquals("foo", el.text) - assertEquals(2, el.elements.size) - - assertEquals("k1: hello", el.elements.get(0).text) - assertEquals("k2: world", el.elements.get(1).text) - - val doc = new SwaggerDocument - doc.set(text) - - val position = el.getPosition(doc) - // after foo: - assertEquals(0, position.offset) - assertEquals(6, position.length) - - // should be first line - assertEquals(0, doc.getLineOfOffset(position.offset)) - } - -// @Test - def void testGet() { - val yaml = ''' - swagger: 2.0 - ''' - - val model = Model.parseYaml(schema, yaml) - - assertEquals("".ptr, model.getPath(0, 0)) - assertEquals("/swagger".ptr, model.getPath(0, 7)) - assertEquals("/swagger".ptr, model.getPath(0, 8)) - } - - def getPath(Model model, int i, int j) { - model.getNode(i, j)?.pointer - } - -// @Test - def void testGetPaths() { - val yaml = ''' - info: - description: "" - version: "1.0.0" - tags: - - foo: "" - - bar: "" - ''' - - val model = Model.parseYaml(schema, yaml) - - assertEquals("/info".ptr, model.getPath(0, 4)) - assertEquals("/info/description".ptr, model.getPath(1, 13)) - assertEquals("/info/version".ptr, model.getPath(2, 9)) - - assertEquals("/tags".ptr, model.getPath(3, 4)) - assertEquals("/tags/0/foo".ptr, model.getPath(4, 8)) - assertEquals("/tags/1/bar".ptr, model.getPath(5, 8)) - } - -// @Test - def void testGetPathOnEmptyLine_1() { - val yaml = ''' - info: - description: "" - - version: "1.0.0" - ''' - - val model = Model.parseYaml(schema, yaml) - - assertEquals("/info/description".ptr, model.getPath(1, 13)) -// assertEquals("/info".ptr, model.getPath(2, 2)) -// assertEquals("/info/version".ptr, model.getPath(3, 9)) - } - -// @Test - def void testGetPathOnEmptyLine() { - val yaml = ''' - info: - description: "" - - version: "1.0.0" - ''' - - val model = Model.parseYaml(schema, yaml) - - assertEquals("".ptr, model.getPath(0, 0)) - assertEquals("/info".ptr, model.getPath(0, 5)) - assertEquals("/info".ptr, model.getPath(1, 2)) - assertEquals("/info/description".ptr, model.getPath(1, 14)) - assertEquals("/info".ptr, model.getPath(2, 2)) - assertEquals("/info".ptr, model.getPath(3, 2)) - assertEquals("/info/version".ptr, model.getPath(3, 10)) - } - -// @Test - def void testGetPathOnEmptyLineAfter() { - val yaml = ''' - info: - description: "" - version: "1.0.0" - - ''' - - val model = Model.parseYaml(schema, yaml) - - assertEquals("/info/description".ptr, model.getPath(1, 14)) - assertEquals("/info/version".ptr, model.getPath(2, 9)) - assertEquals("/info".ptr, model.getPath(3, 2)) - } - -// @Test - def void testGetPathOnPaths() { - val yaml = ''' - paths: - /: - get: - responses: - '200': - ''' - - val model = Model.parseYaml(schema, yaml) - - assertEquals("/paths".ptr, model.getPath(0, 5)); - assertEquals("/paths/~1".ptr, model.getPath(1, 3)); - assertEquals("/paths/~1/get".ptr, model.getPath(2, 7)); - assertEquals("/paths/~1/get/responses".ptr, model.getPath(3, 15)); - assertEquals("/paths/~1/get/responses/200".ptr, model.getPath(4, 13)); - } - -// @Test - def void testGetPathOnPathsAfter() { - val yaml = ''' - paths: - /: - - ''' - - val model = Model.parseYaml(schema, yaml) - - assertEquals("/paths".ptr, model.getPath(0, 5)); - assertEquals("/paths".ptr, model.getPath(1, 1)); - assertEquals("/paths/~1".ptr, model.getPath(1, 3)); - assertEquals("/paths".ptr, model.getPath(2, 3)); - assertEquals("/paths/~1".ptr, model.getPath(2, 4)); - } - -// @Test - def void testGetPath() { - val yaml = ''' - paths: - /: - get: - responses: - '200': - description: OK - - parameters: - foo: - name: foo - ''' - - val model = Model.parseYaml(schema, yaml) - - assertEquals("/paths".ptr, model.getPath(6, 2)) - assertEquals("/paths/~1".ptr, model.getPath(6, 4)) - } - -// @Test - def void testPathResponse() { - val yaml = ''' - paths: - /: - options: - responses: - 200: - description: Ok - ''' - - val model = Model.parseYaml(schema, yaml) - - assertEquals("/paths/~1/options", model.getPath(4, 6)) - } -} diff --git a/com.reprezen.swagedit.tests/src/com/reprezen/swagedit/editor/outline/OutlineStyledLabelProviderTest.xtend b/com.reprezen.swagedit.tests/src/com/reprezen/swagedit/editor/outline/OutlineStyledLabelProviderTest.xtend index c2b21f2a..3efc9c2f 100644 --- a/com.reprezen.swagedit.tests/src/com/reprezen/swagedit/editor/outline/OutlineStyledLabelProviderTest.xtend +++ b/com.reprezen.swagedit.tests/src/com/reprezen/swagedit/editor/outline/OutlineStyledLabelProviderTest.xtend @@ -1,6 +1,5 @@ package com.reprezen.swagedit.editor.outline -import com.reprezen.swagedit.core.model.Model import org.eclipse.swt.graphics.RGB import org.junit.Before import org.junit.Test @@ -8,6 +7,8 @@ import org.junit.Test import static org.junit.Assert.* import com.reprezen.swagedit.schema.SwaggerSchema import com.reprezen.swagedit.core.editor.outline.OutlineStyledLabelProvider +import com.reprezen.swagedit.core.json.JsonModel +import com.fasterxml.jackson.core.JsonPointer class OutlineStyledLabelProviderTest { @@ -30,11 +31,12 @@ class OutlineStyledLabelProviderTest { key: value ''' - val root = Model.parseYaml(schema, text).root - val el = root.elements.get(0).elements.get(0) + val model = new JsonModel(schema, text, false) + val root = model.content + val el = root.get(0) - assertEquals("key: value", el.text) - assertEquals(el.text, provider.getStyledString(el).toString) + assertEquals("key: value", provider.getText(el, JsonPointer.compile("/foo/key"))) + assertEquals("key: value", provider.getStyledString(model, JsonPointer.compile("/foo/key")).toString) } @Test @@ -45,18 +47,22 @@ class OutlineStyledLabelProviderTest { - https ''' - val els = Model.parseYaml(schema, text).root + val model = new JsonModel(schema, text, false) + val els = model.content + println(els) - assertEquals("schemes", els.get(0).text) - assertEquals("schemes schemesList", provider.getStyledString(els.get(0)).toString) + assertEquals("schemes", provider.getText(els, "/schemes".ptr)) + assertEquals("schemes schemesList", provider.getStyledString(model, "/schemes".ptr).toString) - val http = els.get(0).elements.get(0) - assertEquals("http", http.text) - assertEquals("http", provider.getStyledString(http).toString) +println(els.get("schemes")) + val http = els.get("schemes").get(0) + println(http) + assertEquals("http", provider.getText(http, "/schemes/0/http".ptr)) + assertEquals("http", provider.getStyledString(model, "/schemes/0/http".ptr).toString) - val https = els.get(0).elements.get(1) - assertEquals("https", https.text) - assertEquals("https", provider.getStyledString(https).toString) + val https = els.get("schemes").get(1) + assertEquals("https", provider.getText(https, "/schemes/1/https".ptr)) + assertEquals("https", provider.getStyledString(model, "/schemes/1/https".ptr).toString) } @Test @@ -67,10 +73,11 @@ class OutlineStyledLabelProviderTest { value: world ''' - val els = Model.parseYaml(schema, text).root - assertEquals("object", els.get(0).text) + val model = new JsonModel(schema, text, false) + val els = model.content - // TODO + assertEquals("object", provider.getText(els, "/object".ptr)) + // TODO } @Test @@ -83,10 +90,11 @@ class OutlineStyledLabelProviderTest { value: world ''' - val els = Model.parseYaml(schema, text).root - assertEquals("objects", els.get(0).text) + val model = new JsonModel(schema, text, false) + val els = model.content - // TODO + assertEquals("objects", provider.getText(els, "/objects".ptr)) + // TODO } @Test @@ -97,10 +105,14 @@ class OutlineStyledLabelProviderTest { value: world ''' - val els = Model.parseYaml(schema, text).root - assertEquals("object", els.get(0).text) + val model = new JsonModel(schema, text, false) + val els = model.content - // TODO + assertEquals("object", provider.getText(els, "/object".ptr)) + // TODO } + def ptr(String ptr) { + JsonPointer.compile(ptr) + } } diff --git a/com.reprezen.swagedit.tests/src/com/reprezen/swagedit/mocks/Mocks.java b/com.reprezen.swagedit.tests/src/com/reprezen/swagedit/mocks/Mocks.java index bf15847a..51654951 100644 --- a/com.reprezen.swagedit.tests/src/com/reprezen/swagedit/mocks/Mocks.java +++ b/com.reprezen.swagedit.tests/src/com/reprezen/swagedit/mocks/Mocks.java @@ -20,7 +20,6 @@ import com.reprezen.swagedit.core.json.references.JsonDocumentManager; import com.reprezen.swagedit.core.json.references.JsonReference; import com.reprezen.swagedit.core.json.references.JsonReferenceFactory; -import com.reprezen.swagedit.core.model.AbstractNode; import com.reprezen.swagedit.editor.SwaggerDocument; import com.reprezen.swagedit.editor.hyperlinks.SwaggerReferenceHyperlinkDetector; @@ -43,21 +42,14 @@ protected URI getBaseURI() { protected JsonReferenceFactory getFactory() { return new JsonReferenceFactory() { @Override - public JsonReference createSimpleReference(URI baseURI, AbstractNode valueNode) { - JsonReference ref = super.createSimpleReference(baseURI, valueNode); + public JsonReference createSimpleReference(URI baseURI, JsonNode doc, JsonNode valueNode) { + JsonReference ref = super.createSimpleReference(baseURI, doc, valueNode); if (ref != null) { ref.setDocumentManager(manager); } return ref; } - @Override - public JsonReference create(AbstractNode node) { - JsonReference ref = super.create(node); - ref.setDocumentManager(manager); - return ref; - } - @Override public JsonReference create(JsonNode node) { JsonReference ref = super.create(node); @@ -74,7 +66,7 @@ public static JsonReferenceFactory mockJsonReferenceFactory(final Map() { override ICompletionProposal[] apply(IContentAssistProcessor processor, String marker) { diff --git a/com.reprezen.swagedit.tests/src/com/reprezen/swagedit/validation/ErrorProcessorTest.java b/com.reprezen.swagedit.tests/src/com/reprezen/swagedit/validation/ErrorProcessorTest.java index 093a2897..79c3a2a8 100644 --- a/com.reprezen.swagedit.tests/src/com/reprezen/swagedit/validation/ErrorProcessorTest.java +++ b/com.reprezen.swagedit.tests/src/com/reprezen/swagedit/validation/ErrorProcessorTest.java @@ -19,7 +19,6 @@ import org.junit.Before; import org.junit.Test; -import org.yaml.snakeyaml.nodes.Node; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; @@ -33,8 +32,7 @@ public class ErrorProcessorTest { @Before public void setUp() { - Node document = null; - processor = new ErrorProcessor(document, null); + processor = new ErrorProcessor(null, null); } @Test diff --git a/com.reprezen.swagedit.tests/src/com/reprezen/swagedit/validation/MultipleSwaggerErrorMessageTest.xtend b/com.reprezen.swagedit.tests/src/com/reprezen/swagedit/validation/MultipleSwaggerErrorMessageTest.xtend index ec174023..67c318a6 100644 --- a/com.reprezen.swagedit.tests/src/com/reprezen/swagedit/validation/MultipleSwaggerErrorMessageTest.xtend +++ b/com.reprezen.swagedit.tests/src/com/reprezen/swagedit/validation/MultipleSwaggerErrorMessageTest.xtend @@ -1,12 +1,12 @@ package com.reprezen.swagedit.validation import com.fasterxml.jackson.databind.JsonNode +import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.databind.node.ArrayNode import com.google.common.base.Strings import com.google.common.collect.Lists import com.reprezen.swagedit.core.schema.JsonSchemaUtils import com.reprezen.swagedit.schema.SwaggerSchema -import io.swagger.util.Json import java.util.List import org.junit.Test @@ -75,7 +75,7 @@ class MultipleSwaggerErrorMessageTest { } def void assertHumanFriendlyTextForNodeEquals(CharSequence json, String expectedLabel, String defaultValue) { - val JsonNode arrayOfSchemasNode = Json.mapper().readTree(json.toString); + val JsonNode arrayOfSchemasNode = new ObjectMapper().readTree(json.toString); assertNotNull(arrayOfSchemasNode) val label = JsonSchemaUtils::getHumanFriendlyText(arrayOfSchemasNode, defaultValue); assertEquals(expectedLabel, label) diff --git a/com.reprezen.swagedit.tests/src/com/reprezen/swagedit/validation/ReferenceValidatorTest.xtend b/com.reprezen.swagedit.tests/src/com/reprezen/swagedit/validation/ReferenceValidatorTest.xtend index e3cc82a8..56aaebe9 100644 --- a/com.reprezen.swagedit.tests/src/com/reprezen/swagedit/validation/ReferenceValidatorTest.xtend +++ b/com.reprezen.swagedit.tests/src/com/reprezen/swagedit/validation/ReferenceValidatorTest.xtend @@ -1,19 +1,20 @@ package com.reprezen.swagedit.validation import com.fasterxml.jackson.databind.JsonNode +import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.dataformat.yaml.YAMLFactory import com.reprezen.swagedit.core.json.references.JsonReferenceValidator import com.reprezen.swagedit.core.validation.Messages import com.reprezen.swagedit.core.validation.SwaggerError +import com.reprezen.swagedit.core.validation.Validator import com.reprezen.swagedit.editor.SwaggerDocument import com.reprezen.swagedit.mocks.Mocks -import io.swagger.util.Yaml import java.net.URI import java.util.Map import org.eclipse.core.resources.IMarker import org.junit.Test import static org.junit.Assert.* -import com.reprezen.swagedit.core.validation.Validator class ReferenceValidatorTest { @@ -419,7 +420,7 @@ class ReferenceValidatorTest { } def asJson(String string) { - Yaml.mapper().readTree(string) + new ObjectMapper(new YAMLFactory).readTree(string) } } diff --git a/com.reprezen.swagedit.tests/src/com/reprezen/swagedit/validation/ValidatorTest.xtend b/com.reprezen.swagedit.tests/src/com/reprezen/swagedit/validation/ValidatorTest.xtend index 2dc44dff..f236bae7 100644 --- a/com.reprezen.swagedit.tests/src/com/reprezen/swagedit/validation/ValidatorTest.xtend +++ b/com.reprezen.swagedit.tests/src/com/reprezen/swagedit/validation/ValidatorTest.xtend @@ -338,139 +338,139 @@ class ValidatorTest { )) } - @Test - def void correctAliasAfterAnchor() { - val content = ''' - swagger: "2.0" - info: - version: 1.0.0 - title: Uber parameters - - paths: {} - - parameters: - - law: - name: law_applicability - in: query - description: scope of a law - required: false - type: string - enum: &scope_values - - GLOBAL - - REGIONAL - - LOCAL - - brand: - name: brand_reach - in: query - description: where a brand name is used - type: string - enum: *scope_values - ''' - - document.set(content) - document.onChange() - - assertThat(document.yamlError, nullValue) - assertThat(document.jsonError, nullValue) - - val errors = validator.validate(document, null as URI) - assertEquals(0, errors.size()) - } - - @Test - def void correctAliasBeforeAnchor() { - val content = ''' - swagger: "2.0" - info: - version: 1.0.0 - title: Uber parameters - - paths: {} - - parameters: - - brand: - name: brand_reach - in: query - description: where a brand name is used - type: string - enum: *scope_values - - law: - name: law_applicability - in: query - description: scope of a law - required: false - type: string - enum: &scope_values - - GLOBAL - - REGIONAL - - LOCAL - - ''' - - document.set(content) - document.onChange() - - assertThat(document.yamlError, notNullValue) - assertThat(document.yamlError.message, - equalTo( - "found undefined alias scope_values\n in 'reader', line 15, column 11:\n enum: *scope_values\n ^\n")) - - assertThat(document.jsonError, nullValue) - - val errors = validator.validate(document, null as URI) - assertEquals(0, errors.size()) - } - - @Test - def void invlaidAlias() { - val content = ''' - swagger: "2.0" - info: - version: 1.0.0 - title: Uber parameters - - paths: {} - - parameters: - - law: - name: law_applicability - in: query - description: scope of a law - required: false - type: string - enum: &scope_values - - GLOBAL - - REGIONAL - - LOCAL - - brand: - name: brand_reach - in: query - description: where a brand name is used - type: string - enum: *scope_values_BROKEN - ''' - - document.set(content) - document.onChange() - - assertThat(document.yamlError, notNullValue) - println(document.yamlError) - assertThat(document.yamlError.message, - equalTo( - "found undefined alias scope_values_BROKEN\n in 'reader', line 26, column 11:\n enum: *scope_values_BROKEN\n ^\n")) - - assertThat(document.jsonError, nullValue) - - val errors = validator.validate(document, null as URI) - assertEquals(0, errors.size()) - - } +// @Test +// def void correctAliasAfterAnchor() { +// val content = ''' +// swagger: "2.0" +// info: +// version: 1.0.0 +// title: Uber parameters +// +// paths: {} +// +// parameters: +// +// law: +// name: law_applicability +// in: query +// description: scope of a law +// required: false +// type: string +// enum: &scope_values +// - GLOBAL +// - REGIONAL +// - LOCAL +// +// brand: +// name: brand_reach +// in: query +// description: where a brand name is used +// type: string +// enum: *scope_values +// ''' +// +// document.set(content) +// document.onChange() +// +// assertThat(document.yamlError, nullValue) +// assertThat(document.jsonError, nullValue) +// +// val errors = validator.validate(document, null as URI) +// assertEquals(0, errors.size()) +// } +// +// @Test +// def void correctAliasBeforeAnchor() { +// val content = ''' +// swagger: "2.0" +// info: +// version: 1.0.0 +// title: Uber parameters +// +// paths: {} +// +// parameters: +// +// brand: +// name: brand_reach +// in: query +// description: where a brand name is used +// type: string +// enum: *scope_values +// +// law: +// name: law_applicability +// in: query +// description: scope of a law +// required: false +// type: string +// enum: &scope_values +// - GLOBAL +// - REGIONAL +// - LOCAL +// +// ''' +// +// document.set(content) +// document.onChange() +// +// assertThat(document.yamlError, notNullValue) +// assertThat(document.yamlError.message, +// equalTo( +// "found undefined alias scope_values\n in 'reader', line 15, column 11:\n enum: *scope_values\n ^\n")) +// +// assertThat(document.jsonError, nullValue) +// +// val errors = validator.validate(document, null as URI) +// assertEquals(0, errors.size()) +// } +// +// @Test +// def void invlaidAlias() { +// val content = ''' +// swagger: "2.0" +// info: +// version: 1.0.0 +// title: Uber parameters +// +// paths: {} +// +// parameters: +// +// law: +// name: law_applicability +// in: query +// description: scope of a law +// required: false +// type: string +// enum: &scope_values +// - GLOBAL +// - REGIONAL +// - LOCAL +// +// brand: +// name: brand_reach +// in: query +// description: where a brand name is used +// type: string +// enum: *scope_values_BROKEN +// ''' +// +// document.set(content) +// document.onChange() +// +// assertThat(document.yamlError, notNullValue) +// println(document.yamlError) +// assertThat(document.yamlError.message, +// equalTo( +// "found undefined alias scope_values_BROKEN\n in 'reader', line 26, column 11:\n enum: *scope_values_BROKEN\n ^\n")) +// +// assertThat(document.jsonError, nullValue) +// +// val errors = validator.validate(document, null as URI) +// assertEquals(0, errors.size()) +// +// } @Test def void testArrayWithItemsAreValid() { diff --git a/com.reprezen.swagedit.tests/src/com/reprezen/swagedit/validation/YamlErrorProcessorTest.xtend b/com.reprezen.swagedit.tests/src/com/reprezen/swagedit/validation/YamlErrorProcessorTest.xtend index 9755eb8f..ff3954e0 100644 --- a/com.reprezen.swagedit.tests/src/com/reprezen/swagedit/validation/YamlErrorProcessorTest.xtend +++ b/com.reprezen.swagedit.tests/src/com/reprezen/swagedit/validation/YamlErrorProcessorTest.xtend @@ -1,11 +1,12 @@ package com.reprezen.swagedit.validation +import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.dataformat.yaml.YAMLFactory +import com.reprezen.swagedit.core.validation.Messages import com.reprezen.swagedit.core.validation.YamlErrorProcessor -import io.swagger.util.Yaml import org.junit.Test import static org.junit.Assert.* -import com.reprezen.swagedit.core.validation.Messages class YamlErrorProcessorTest { @@ -32,7 +33,7 @@ class YamlErrorProcessorTest { type: string ''' - var mapper = Yaml.mapper + var mapper = new ObjectMapper(new YAMLFactory) try { mapper.readTree(content) fail() diff --git a/com.reprezen.swagedit/.classpath b/com.reprezen.swagedit/.classpath index 65d7608b..b9a5b1ec 100644 --- a/com.reprezen.swagedit/.classpath +++ b/com.reprezen.swagedit/.classpath @@ -1,7 +1,5 @@ - - diff --git a/com.reprezen.swagedit/META-INF/MANIFEST.MF b/com.reprezen.swagedit/META-INF/MANIFEST.MF index 7d95fc28..57f45d02 100644 --- a/com.reprezen.swagedit/META-INF/MANIFEST.MF +++ b/com.reprezen.swagedit/META-INF/MANIFEST.MF @@ -16,16 +16,9 @@ Require-Bundle: org.eclipse.ui, com.github.eclipsecolortheme;resolution:=optional, com.reprezen.swagedit.core, com.google.guava;bundle-version="15.0.0", - com.fasterxml.jackson.core.jackson-annotations;bundle-version="2.5.0", - com.fasterxml.jackson.core.jackson-core;bundle-version="2.5.0", - com.fasterxml.jackson.core.jackson-databind;bundle-version="2.5.0", - com.fasterxml.jackson.dataformat.jackson-dataformat-yaml;bundle-version="2.5.0", org.slf4j.api;bundle-version="1.7.2" Bundle-ActivationPolicy: lazy -Bundle-RequiredExecutionEnvironment: JavaSE-1.7 -Bundle-ClassPath: ., - lib/swagger-core-1.5.14.jar, - lib/swagger-models-1.5.14.jar +Bundle-RequiredExecutionEnvironment: JavaSE-1.7 Bundle-Activator: com.reprezen.swagedit.Activator Export-Package: com.reprezen.swagedit, com.reprezen.swagedit.assist, @@ -33,6 +26,7 @@ Export-Package: com.reprezen.swagedit, com.reprezen.swagedit.editor.hyperlinks, com.reprezen.swagedit.preferences, com.reprezen.swagedit.quickfix, + com.reprezen.swagedit.schema, com.reprezen.swagedit.templates, com.reprezen.swagedit.wizards Bundle-Vendor: ModelSolv, Inc. d.b.a. RepreZen diff --git a/com.reprezen.swagedit/build.properties b/com.reprezen.swagedit/build.properties index 6e3f0cc0..96fc6285 100644 --- a/com.reprezen.swagedit/build.properties +++ b/com.reprezen.swagedit/build.properties @@ -4,7 +4,10 @@ bin.includes = .,\ icons/,\ resources/,\ OSGI-INF/,\ - lib/swagger-core-1.5.14.jar,\ - lib/swagger-models-1.5.14.jar + lib/com.fasterxml.jackson.core.jackson-annotations_2.9.0.jar,\ + lib/com.fasterxml.jackson.core.jackson-core_2.9.2.jar,\ + lib/com.fasterxml.jackson.core.jackson-databind_2.9.2.jar,\ + lib/com.fasterxml.jackson.dataformat.jackson-dataformat-yaml_2.9.2.jar,\ + lib/org.yaml.snakeyaml_1.18.0.jar jars.compile.order = . source.. = src/ diff --git a/com.reprezen.swagedit/lib/swagger-core-1.5.14.jar b/com.reprezen.swagedit/lib/swagger-core-1.5.14.jar deleted file mode 100644 index dd971112..00000000 Binary files a/com.reprezen.swagedit/lib/swagger-core-1.5.14.jar and /dev/null differ diff --git a/com.reprezen.swagedit/lib/swagger-models-1.5.14.jar b/com.reprezen.swagedit/lib/swagger-models-1.5.14.jar deleted file mode 100644 index 1929f02d..00000000 Binary files a/com.reprezen.swagedit/lib/swagger-models-1.5.14.jar and /dev/null differ diff --git a/com.reprezen.swagedit/src/com/reprezen/swagedit/assist/SwaggerContentAssistProcessor.java b/com.reprezen.swagedit/src/com/reprezen/swagedit/assist/SwaggerContentAssistProcessor.java index fd79291f..8d0178b9 100644 --- a/com.reprezen.swagedit/src/com/reprezen/swagedit/assist/SwaggerContentAssistProcessor.java +++ b/com.reprezen.swagedit/src/com/reprezen/swagedit/assist/SwaggerContentAssistProcessor.java @@ -19,7 +19,7 @@ import com.reprezen.swagedit.core.assist.JsonProposalProvider; import com.reprezen.swagedit.core.assist.ext.MediaTypeContentAssistExt; import com.reprezen.swagedit.core.assist.ext.ResponseCodeContentAssistExt; -import com.reprezen.swagedit.core.model.Model; +import com.reprezen.swagedit.core.editor.JsonDocument; import com.reprezen.swagedit.templates.SwaggerContextType; /** @@ -47,7 +47,7 @@ protected ContextTypeRegistry getContextTypeRegistry() { } @Override - protected String getContextTypeId(Model model, String path) { + protected String getContextTypeId(JsonDocument doc, String path) { return SwaggerContextType.getContextType(path); } diff --git a/com.reprezen.swagedit/src/com/reprezen/swagedit/assist/SwaggerReferenceProposalProvider.java b/com.reprezen.swagedit/src/com/reprezen/swagedit/assist/SwaggerReferenceProposalProvider.java index 9340a906..4c31b771 100644 --- a/com.reprezen.swagedit/src/com/reprezen/swagedit/assist/SwaggerReferenceProposalProvider.java +++ b/com.reprezen.swagedit/src/com/reprezen/swagedit/assist/SwaggerReferenceProposalProvider.java @@ -32,11 +32,11 @@ public SwaggerReferenceProposalProvider() { protected static final String PATH_ITEM_REGEX = "/paths/~1[^/]+/\\$ref"; - public static final ContextType SCHEMA_DEFINITION = new RegexContextType("definitions", "schemas", + public static final ContextType SCHEMA_DEFINITION = new RegexContextType("/definitions", "schemas", SCHEMA_DEFINITION_REGEX); - public static final ContextType PATH_ITEM = new RegexContextType("paths", "path items", PATH_ITEM_REGEX); - public static final ContextType PATH_PARAMETER = new RegexContextType("parameters", "parameters", PARAMETER_REGEX); - public static final ContextType PATH_RESPONSE = new RegexContextType("responses", "responses", RESPONSE_REGEX); + public static final ContextType PATH_ITEM = new RegexContextType("/paths", "path items", PATH_ITEM_REGEX); + public static final ContextType PATH_PARAMETER = new RegexContextType("/parameters", "parameters", PARAMETER_REGEX); + public static final ContextType PATH_RESPONSE = new RegexContextType("/responses", "responses", RESPONSE_REGEX); public static final ContextTypeCollection SWAGGER_CONTEXT_TYPES = ContextType .newContentTypeCollection(Lists.newArrayList( // diff --git a/com.reprezen.swagedit/src/com/reprezen/swagedit/editor/SwaggerDocument.java b/com.reprezen.swagedit/src/com/reprezen/swagedit/editor/SwaggerDocument.java index 53935b54..0bd9db39 100644 --- a/com.reprezen.swagedit/src/com/reprezen/swagedit/editor/SwaggerDocument.java +++ b/com.reprezen.swagedit/src/com/reprezen/swagedit/editor/SwaggerDocument.java @@ -21,7 +21,7 @@ public class SwaggerDocument extends JsonDocument { public SwaggerDocument() { - super(io.swagger.util.Yaml.mapper(), Activator.getDefault() != null ? Activator.getDefault().getSchema() : new SwaggerSchema()); + super(Activator.getDefault() != null ? Activator.getDefault().getSchema() : new SwaggerSchema()); } }