Basic concourse pipeline editor working
This commit is contained in:
@@ -94,6 +94,7 @@ public class SimpleTextDocumentService implements TextDocumentService {
|
||||
try {
|
||||
VersionedTextDocumentIdentifier docId = params.getTextDocument();
|
||||
String url = docId.getUri();
|
||||
LOG.info("didChange: "+url);
|
||||
if (url!=null) {
|
||||
TextDocument doc = getOrCreateDocument(url);
|
||||
for (TextDocumentContentChangeEvent change : params.getContentChanges()) {
|
||||
@@ -108,7 +109,7 @@ public class SimpleTextDocumentService implements TextDocumentService {
|
||||
|
||||
@Override
|
||||
public void didOpen(DidOpenTextDocumentParams params) {
|
||||
//LOG.info("didOpen: "+params);
|
||||
LOG.info("didOpen: "+params.getUri());
|
||||
//Example message:
|
||||
//{
|
||||
// "jsonrpc":"2.0",
|
||||
@@ -150,7 +151,7 @@ public class SimpleTextDocumentService implements TextDocumentService {
|
||||
|
||||
@Override
|
||||
public void didClose(DidCloseTextDocumentParams params) {
|
||||
System.out.println("closing: "+params.getTextDocument().getUri());
|
||||
LOG.info("didClose: "+params.getTextDocument().getUri());
|
||||
String url = params.getTextDocument().getUri();
|
||||
if (url!=null) {
|
||||
documents.remove(url);
|
||||
|
||||
@@ -241,42 +241,52 @@ public class Renderables {
|
||||
}
|
||||
|
||||
public static Renderable fromClasspath(final Class<?> klass, final String resourcePath) {
|
||||
return new Renderable() {
|
||||
|
||||
@Override
|
||||
public void renderAsMarkdown(StringBuilder buffer) {
|
||||
String extension = ".md";
|
||||
String value = getText(klass, resourcePath, extension);
|
||||
if (value != null) {
|
||||
buffer.append(value);
|
||||
} else {
|
||||
NO_DESCRIPTION.renderAsMarkdown(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderAsHtml(HtmlBuffer buffer) {
|
||||
String extension = ".html";
|
||||
String value = getText(klass, resourcePath, extension);
|
||||
if (value != null) {
|
||||
buffer.raw(value);
|
||||
} else {
|
||||
NO_DESCRIPTION.renderAsHtml(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
private String getText(final Class<?> klass, final String resourcePath, String extension) {
|
||||
try {
|
||||
InputStream stream = klass.getResourceAsStream(resourcePath + extension);
|
||||
if (stream != null) {
|
||||
return IOUtil.toString(stream);
|
||||
if (resourcePath.endsWith(".html")) {
|
||||
return htmlBlob((HtmlBuffer html) -> {
|
||||
html.raw(getText(klass, resourcePath, null));
|
||||
});
|
||||
} else {
|
||||
return new Renderable() {
|
||||
|
||||
@Override
|
||||
public void renderAsMarkdown(StringBuilder buffer) {
|
||||
String extension = ".md";
|
||||
String value = getText(klass, resourcePath, extension);
|
||||
if (value != null) {
|
||||
buffer.append(value);
|
||||
} else {
|
||||
NO_DESCRIPTION.renderAsMarkdown(buffer);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("Error", e);
|
||||
}
|
||||
return null;
|
||||
|
||||
@Override
|
||||
public void renderAsHtml(HtmlBuffer buffer) {
|
||||
String extension = ".html";
|
||||
String value = getText(klass, resourcePath, extension);
|
||||
if (value != null) {
|
||||
buffer.raw(value);
|
||||
} else {
|
||||
NO_DESCRIPTION.renderAsHtml(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private static String getText(final Class<?> klass, String resourcePath, String extension) {
|
||||
if (extension!=null) {
|
||||
resourcePath = resourcePath + extension;
|
||||
}
|
||||
try {
|
||||
InputStream stream = klass.getResourceAsStream(resourcePath);
|
||||
if (stream != null) {
|
||||
return IOUtil.toString(stream);
|
||||
}
|
||||
};
|
||||
} catch (Exception e) {
|
||||
logger.error("Error", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static class ConcatRenderables implements Renderable {
|
||||
|
||||
@@ -41,6 +41,8 @@ public abstract class AbstractYamlAssistContext implements YamlAssistContext {
|
||||
|
||||
public final int documentSelector;
|
||||
public final YamlPath contextPath;
|
||||
private final YamlDocument doc;
|
||||
|
||||
|
||||
private static PrefixFinder prefixfinder = new PrefixFinder() {
|
||||
protected boolean isPrefixChar(char c) {
|
||||
@@ -48,6 +50,10 @@ public abstract class AbstractYamlAssistContext implements YamlAssistContext {
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public YamlDocument getDocument() {
|
||||
return doc;
|
||||
}
|
||||
|
||||
protected final String getPrefix(YamlDocument doc, SNode node, int offset) {
|
||||
//For value completions... in general we would like to determine the whole text
|
||||
@@ -78,7 +84,8 @@ public abstract class AbstractYamlAssistContext implements YamlAssistContext {
|
||||
}
|
||||
|
||||
|
||||
public AbstractYamlAssistContext(int documentSelector, YamlPath contextPath) {
|
||||
public AbstractYamlAssistContext(YamlDocument doc, int documentSelector, YamlPath contextPath) {
|
||||
this.doc = doc;
|
||||
this.documentSelector = documentSelector;
|
||||
this.contextPath = contextPath;
|
||||
}
|
||||
|
||||
@@ -33,6 +33,11 @@ public class SchemaBasedYamlAssistContextProvider implements YamlAssistContextPr
|
||||
protected YamlAssistContext getDocumentContext(int documentSelector) {
|
||||
return new YTypeAssistContext(this, documentSelector, schema.getTopLevelType(), schema.getTypeUtil());
|
||||
}
|
||||
|
||||
@Override
|
||||
public YamlDocument getDocument() {
|
||||
return doc;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,6 @@ package org.springframework.ide.vscode.commons.yaml.completion;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
@@ -25,18 +24,20 @@ import org.springframework.ide.vscode.commons.languageserver.completion.IComplet
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.DocumentRegion;
|
||||
import org.springframework.ide.vscode.commons.util.CollectionUtil;
|
||||
import org.springframework.ide.vscode.commons.util.FuzzyMatcher;
|
||||
import org.springframework.ide.vscode.commons.util.Log;
|
||||
import org.springframework.ide.vscode.commons.util.Renderable;
|
||||
import org.springframework.ide.vscode.commons.yaml.hover.YPropertyInfoTemplates;
|
||||
import org.springframework.ide.vscode.commons.yaml.path.YamlPath;
|
||||
import org.springframework.ide.vscode.commons.yaml.path.YamlPathSegment;
|
||||
import org.springframework.ide.vscode.commons.yaml.path.YamlPathSegment.YamlPathSegmentType;
|
||||
import org.springframework.ide.vscode.commons.yaml.schema.DynamicSchemaContext;
|
||||
import org.springframework.ide.vscode.commons.yaml.schema.SNodeDynamicSchemaContext;
|
||||
import org.springframework.ide.vscode.commons.yaml.schema.YType;
|
||||
import org.springframework.ide.vscode.commons.yaml.schema.YTypeUtil;
|
||||
import org.springframework.ide.vscode.commons.yaml.schema.YTypedProperty;
|
||||
import org.springframework.ide.vscode.commons.yaml.schema.YValueHint;
|
||||
import org.springframework.ide.vscode.commons.yaml.structure.YamlDocument;
|
||||
import org.springframework.ide.vscode.commons.yaml.structure.YamlStructureParser.SChildBearingNode;
|
||||
import org.springframework.ide.vscode.commons.yaml.structure.YamlStructureParser.SKeyNode;
|
||||
import org.springframework.ide.vscode.commons.yaml.structure.YamlStructureParser.SNode;
|
||||
import org.springframework.ide.vscode.commons.yaml.util.YamlIndentUtil;
|
||||
|
||||
@@ -49,14 +50,14 @@ public class YTypeAssistContext extends AbstractYamlAssistContext {
|
||||
final private YamlAssistContext parent;
|
||||
|
||||
public YTypeAssistContext(YTypeAssistContext parent, YamlPath contextPath, YType YType, YTypeUtil typeUtil) {
|
||||
super(parent.documentSelector, contextPath);
|
||||
super(parent.getDocument(), parent.documentSelector, contextPath);
|
||||
this.parent = parent;
|
||||
this.type = YType;
|
||||
this.typeUtil = typeUtil;
|
||||
}
|
||||
|
||||
public YTypeAssistContext(TopLevelAssistContext parent, int documentSelector, YType type, YTypeUtil typeUtil) {
|
||||
super(documentSelector, YamlPath.EMPTY);
|
||||
super(parent.getDocument(), documentSelector, YamlPath.EMPTY);
|
||||
this.type = type;
|
||||
this.typeUtil = typeUtil;
|
||||
this.parent = parent;
|
||||
@@ -74,11 +75,12 @@ public class YTypeAssistContext extends AbstractYamlAssistContext {
|
||||
|
||||
public List<ICompletionProposal> getKeyCompletions(YamlDocument doc, int offset, String query) throws Exception {
|
||||
int queryOffset = offset - query.length();
|
||||
List<YTypedProperty> properties = typeUtil.getProperties(type);
|
||||
SNode contextNode = getContextNode(doc);
|
||||
DynamicSchemaContext dynamicCtxt = new SNodeDynamicSchemaContext(contextNode);
|
||||
List<YTypedProperty> properties = typeUtil.getProperties(type, dynamicCtxt);
|
||||
if (CollectionUtil.hasElements(properties)) {
|
||||
ArrayList<ICompletionProposal> proposals = new ArrayList<>(properties.size());
|
||||
SNode contextNode = getContextNode(doc);
|
||||
Set<String> definedProps = getDefinedProperties(contextNode);
|
||||
Set<String> definedProps = dynamicCtxt.getDefinedProperties();
|
||||
for (YTypedProperty p : properties) {
|
||||
String name = p.getName();
|
||||
double score = FuzzyMatcher.matchScore(query, name);
|
||||
@@ -140,26 +142,6 @@ public class YTypeAssistContext extends AbstractYamlAssistContext {
|
||||
}
|
||||
}
|
||||
|
||||
private Set<String> getDefinedProperties(SNode contextNode) {
|
||||
try {
|
||||
if (contextNode instanceof SChildBearingNode) {
|
||||
List<SNode> children = ((SChildBearingNode)contextNode).getChildren();
|
||||
if (CollectionUtil.hasElements(children)) {
|
||||
Set<String> keys = new HashSet<>(children.size());
|
||||
for (SNode c : children) {
|
||||
if (c instanceof SKeyNode) {
|
||||
keys.add(((SKeyNode) c).getKey());
|
||||
}
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("Error getting defined props", e);
|
||||
}
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
private List<ICompletionProposal> getValueCompletions(YamlDocument doc, int offset, String query) {
|
||||
YValueHint[] values = typeUtil.getHintValues(type);
|
||||
if (values!=null) {
|
||||
@@ -179,13 +161,15 @@ public class YTypeAssistContext extends AbstractYamlAssistContext {
|
||||
}
|
||||
|
||||
@Override
|
||||
public YamlAssistContext traverse(YamlPathSegment s) {
|
||||
public YamlAssistContext traverse(YamlPathSegment s) throws Exception {
|
||||
if (s.getType()==YamlPathSegmentType.VAL_AT_KEY) {
|
||||
if (typeUtil.isSequencable(type) || typeUtil.isMap(type)) {
|
||||
return contextWith(s, typeUtil.getDomainType(type));
|
||||
}
|
||||
String key = s.toPropString();
|
||||
Map<String, YTypedProperty> subproperties = typeUtil.getPropertiesMap(type);
|
||||
SNode contextNode = getContextNode(getDocument());
|
||||
DynamicSchemaContext dynamicCtxt = new SNodeDynamicSchemaContext(contextNode);
|
||||
Map<String, YTypedProperty> subproperties = typeUtil.getPropertiesMap(type, dynamicCtxt);
|
||||
if (subproperties!=null) {
|
||||
return contextWith(s, getType(subproperties.get(key)));
|
||||
}
|
||||
@@ -246,6 +230,16 @@ public class YTypeAssistContext extends AbstractYamlAssistContext {
|
||||
return null;
|
||||
}
|
||||
|
||||
private DynamicSchemaContext getSchemaContext() {
|
||||
try {
|
||||
SNode contextNode = getContextNode(getDocument());
|
||||
return new SNodeDynamicSchemaContext(contextNode);
|
||||
} catch (Exception e) {
|
||||
Log.log(e);
|
||||
return DynamicSchemaContext.NULL;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Renderable getValueHoverInfo(YamlDocument doc, DocumentRegion documentRegion) {
|
||||
//By default we don't provide value-specific hover, so just show the same hover
|
||||
@@ -254,6 +248,6 @@ public class YTypeAssistContext extends AbstractYamlAssistContext {
|
||||
}
|
||||
|
||||
private YTypedProperty getProperty(String name) {
|
||||
return typeUtil.getPropertiesMap(getType()).get(name);
|
||||
return typeUtil.getPropertiesMap(getType(), getSchemaContext()).get(name);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,4 +32,6 @@ public interface YamlAssistContext extends YamlNavigable<YamlAssistContext> {
|
||||
Renderable getHoverInfo(YamlPathSegment lastSegment);
|
||||
|
||||
Renderable getValueHoverInfo(YamlDocument doc, DocumentRegion documentRegion);
|
||||
|
||||
YamlDocument getDocument();
|
||||
}
|
||||
|
||||
@@ -10,6 +10,8 @@ import org.springframework.ide.vscode.commons.util.StringUtil;
|
||||
import org.springframework.ide.vscode.commons.util.ValueParser;
|
||||
import org.springframework.ide.vscode.commons.yaml.ast.NodeUtil;
|
||||
import org.springframework.ide.vscode.commons.yaml.ast.YamlFileAST;
|
||||
import org.springframework.ide.vscode.commons.yaml.schema.ASTDynamicSchemaContext;
|
||||
import org.springframework.ide.vscode.commons.yaml.schema.DynamicSchemaContext;
|
||||
import org.springframework.ide.vscode.commons.yaml.schema.YType;
|
||||
import org.springframework.ide.vscode.commons.yaml.schema.YTypeUtil;
|
||||
import org.springframework.ide.vscode.commons.yaml.schema.YTypedProperty;
|
||||
@@ -53,7 +55,8 @@ public class SchemaBasedYamlASTReconciler implements YamlASTReconciler {
|
||||
reconcile(entry.getValueNode(), typeUtil.getDomainType(type));
|
||||
}
|
||||
} else if (typeUtil.isBean(type)) {
|
||||
Map<String, YTypedProperty> beanProperties = typeUtil.getPropertiesMap(type);
|
||||
DynamicSchemaContext schemaContext = new ASTDynamicSchemaContext(map);
|
||||
Map<String, YTypedProperty> beanProperties = typeUtil.getPropertiesMap(type, schemaContext);
|
||||
for (NodeTuple entry : map.getValue()) {
|
||||
Node keyNode = entry.getKeyNode();
|
||||
String key = NodeUtil.asScalar(keyNode);
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2015, 2016 Pivotal, Inc.
|
||||
* 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:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.commons.yaml.schema;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.ide.vscode.commons.yaml.ast.NodeUtil;
|
||||
import org.yaml.snakeyaml.nodes.MappingNode;
|
||||
import org.yaml.snakeyaml.nodes.NodeTuple;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
|
||||
/**
|
||||
* Adapts a SnakeYaml ast node as a {@link DynamicSchemaContext} (so it
|
||||
* can be used in YamlSchema based reconciler.
|
||||
*
|
||||
* @author Kris De Volder
|
||||
*/
|
||||
public class ASTDynamicSchemaContext extends CachingSchemaContext {
|
||||
|
||||
private MappingNode mapNode;
|
||||
|
||||
public ASTDynamicSchemaContext(MappingNode map) {
|
||||
this.mapNode = map;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Set<String> computeDefinedProperties() {
|
||||
if (mapNode!=null) {
|
||||
ImmutableSet.Builder<String> builder = ImmutableSet.builder();
|
||||
for (NodeTuple entry : mapNode.getValue()) {
|
||||
String key = NodeUtil.asScalar(entry.getKeyNode());
|
||||
if (key!=null) { //key not a scalar? => something funky so skip it
|
||||
builder.add(key);
|
||||
}
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
return Collections.emptySet();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package org.springframework.ide.vscode.commons.yaml.schema;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
public abstract class CachingSchemaContext implements DynamicSchemaContext {
|
||||
|
||||
private Set<String> definedProps;
|
||||
|
||||
protected abstract Set<String> computeDefinedProperties();
|
||||
|
||||
@Override
|
||||
final public Set<String> getDefinedProperties() {
|
||||
if (definedProps==null) {
|
||||
definedProps = computeDefinedProperties();
|
||||
}
|
||||
return definedProps;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2015, 2016 Pivotal, Inc.
|
||||
* 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:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.commons.yaml.schema;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
|
||||
/**
|
||||
* Exposes some 'dynamic' contextual information to schema implementations.
|
||||
* <p>
|
||||
* This is necessary to enable the impementation of schema features where one
|
||||
* part of a yaml structure depends details of another part. A typical example
|
||||
* could be 'type' property, so that different values assigned to that property
|
||||
* change the structure that is expected for the rest of the node.
|
||||
*
|
||||
* @author Kris De Volder
|
||||
*/
|
||||
public interface DynamicSchemaContext {
|
||||
|
||||
DynamicSchemaContext NULL = new DynamicSchemaContext() {
|
||||
@Override
|
||||
public Set<String> getDefinedProperties() {
|
||||
return ImmutableSet.of();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the set of property names that are already defined in the current context.
|
||||
* <p>
|
||||
* In Content assist scenarios this is only a 'best effort' result. An empty set may
|
||||
* be returned because either no properties are yet defined, or because the sloppy parser
|
||||
* can not quite make out the yaml structure because yaml text is incomplete or too complex
|
||||
* for its analysis.
|
||||
* <p>
|
||||
* In a validation scenario however, the returned information should precisely reflect what
|
||||
* properties are defined in the surrounding object.
|
||||
*/
|
||||
Set<String> getDefinedProperties();
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2015, 2016 Pivotal, Inc.
|
||||
* 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:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.commons.yaml.schema;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.ide.vscode.commons.util.CollectionUtil;
|
||||
import org.springframework.ide.vscode.commons.util.Log;
|
||||
import org.springframework.ide.vscode.commons.yaml.structure.YamlStructureParser.SChildBearingNode;
|
||||
import org.springframework.ide.vscode.commons.yaml.structure.YamlStructureParser.SKeyNode;
|
||||
import org.springframework.ide.vscode.commons.yaml.structure.YamlStructureParser.SNode;
|
||||
|
||||
/**
|
||||
* Adapts an SNode so it can be used by a YamlSchema as a {@link DynamicSchemaContext}
|
||||
*
|
||||
* @author Kris De Volder
|
||||
*/
|
||||
public class SNodeDynamicSchemaContext extends CachingSchemaContext {
|
||||
|
||||
private SNode contextNode;
|
||||
|
||||
public SNodeDynamicSchemaContext(SNode contextNode) {
|
||||
this.contextNode = contextNode;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Set<String> computeDefinedProperties() {
|
||||
try {
|
||||
if (contextNode instanceof SChildBearingNode) {
|
||||
List<SNode> children = ((SChildBearingNode)contextNode).getChildren();
|
||||
if (CollectionUtil.hasElements(children)) {
|
||||
Set<String> keys = new HashSet<>(children.size());
|
||||
for (SNode c : children) {
|
||||
if (c instanceof SKeyNode) {
|
||||
keys.add(((SKeyNode) c).getKey());
|
||||
}
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.log(e);
|
||||
}
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -13,19 +13,26 @@ package org.springframework.ide.vscode.commons.yaml.schema;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.inject.Provider;
|
||||
|
||||
import org.springframework.ide.vscode.commons.util.Assert;
|
||||
import org.springframework.ide.vscode.commons.util.EnumValueParser;
|
||||
import org.springframework.ide.vscode.commons.util.Renderable;
|
||||
import org.springframework.ide.vscode.commons.util.Renderables;
|
||||
import org.springframework.ide.vscode.commons.util.ValueParser;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableList.Builder;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
||||
/**
|
||||
* Static utility method for creating YType objects representing either
|
||||
* 'array-like', 'map-like' or 'object-like' types which can be used
|
||||
@@ -35,6 +42,10 @@ import org.springframework.ide.vscode.commons.util.ValueParser;
|
||||
*/
|
||||
public class YTypeFactory {
|
||||
|
||||
public YType yany(String name) {
|
||||
return new YAny(name);
|
||||
}
|
||||
|
||||
public YType yseq(YType el) {
|
||||
return new YSeqType(el);
|
||||
}
|
||||
@@ -47,6 +58,14 @@ public class YTypeFactory {
|
||||
return new YBeanType(name, properties);
|
||||
}
|
||||
|
||||
public YType yunion(String name, YBeanType... types) {
|
||||
Assert.isLegal(types.length>0);
|
||||
if (types.length==1) {
|
||||
return types[0];
|
||||
}
|
||||
return new YBeanUnionType(name, types);
|
||||
}
|
||||
|
||||
/**
|
||||
* YTypeUtil instances capable of 'interpreting' the YType objects created by this
|
||||
* YTypeFactory
|
||||
@@ -69,13 +88,13 @@ public class YTypeFactory {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, YTypedProperty> getPropertiesMap(YType type) {
|
||||
return ((AbstractType)type).getPropertiesMap();
|
||||
public Map<String, YTypedProperty> getPropertiesMap(YType type, DynamicSchemaContext dc) {
|
||||
return ((AbstractType)type).getPropertiesMap(dc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<YTypedProperty> getProperties(YType type) {
|
||||
return ((AbstractType)type).getProperties();
|
||||
public List<YTypedProperty> getProperties(YType type, DynamicSchemaContext dc) {
|
||||
return ((AbstractType)type).getProperties(dc);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -164,11 +183,11 @@ public class YTypeFactory {
|
||||
}
|
||||
}
|
||||
|
||||
public final List<YTypedProperty> getProperties() {
|
||||
public List<YTypedProperty> getProperties(DynamicSchemaContext dc) {
|
||||
return Collections.unmodifiableList(propertyList);
|
||||
}
|
||||
|
||||
public final Map<String, YTypedProperty> getPropertiesMap() {
|
||||
public Map<String, YTypedProperty> getPropertiesMap(DynamicSchemaContext dc) {
|
||||
if (cachedPropertyMap==null) {
|
||||
cachedPropertyMap = new LinkedHashMap<>();
|
||||
for (YTypedProperty p : propertyList) {
|
||||
@@ -212,12 +231,55 @@ public class YTypeFactory {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void addHints(YValueHint... extraHints) {
|
||||
for (YValueHint h : extraHints) {
|
||||
if (!hints.contains(h)) {
|
||||
hints.add(h);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void parseWith(ValueParser parser) {
|
||||
this.parser = parser;
|
||||
}
|
||||
public ValueParser getParser() {
|
||||
return parser;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a type that is completely unconstrained. Anything goes: A map, a sequence or some
|
||||
* atomic value.
|
||||
*/
|
||||
public static class YAny extends AbstractType {
|
||||
private final String name;
|
||||
|
||||
public YAny(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAtomic() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSequenceable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMap() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class YMapType extends AbstractType {
|
||||
@@ -293,6 +355,26 @@ public class YTypeFactory {
|
||||
public boolean isBean() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deprecated. This method disregards {@link DynamicSchemaContext}. This may be
|
||||
* alright for Schemas which don't rely on it but it will result in incorrect/inaccurate
|
||||
* behavior for Schemas that do.
|
||||
*/
|
||||
@Deprecated
|
||||
public List<YTypedProperty> getProperties() {
|
||||
return getProperties(DynamicSchemaContext.NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deprecated. This method disregards {@link DynamicSchemaContext}. This may be
|
||||
* alright for Schemas which don't rely on it but it will result in incorrect/inaccurate
|
||||
* behavior for Schemas that do.
|
||||
*/
|
||||
@Deprecated
|
||||
public Map<String, YTypedProperty> getPropertiesMap() {
|
||||
return getPropertiesMap(DynamicSchemaContext.NULL);
|
||||
}
|
||||
}
|
||||
|
||||
public static class YAtomicType extends AbstractType {
|
||||
@@ -309,6 +391,103 @@ public class YTypeFactory {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a union of several bean types. It is assumed one primary property
|
||||
* exists in each of the the sub-bean types that can be used to identify the
|
||||
* type. In other words the primary property has a unique name so that when
|
||||
* this property is being assigned a value we can infer from that which
|
||||
* specific bean-type we are dealing with.
|
||||
*/
|
||||
public class YBeanUnionType extends AbstractType {
|
||||
private final String name;
|
||||
|
||||
private Map<String, AbstractType> typesByPrimary = new HashMap<>();
|
||||
|
||||
private ImmutableList<YTypedProperty> primaryProps;
|
||||
|
||||
public YBeanUnionType(String name, YBeanType... types) {
|
||||
this.name = name;
|
||||
for (YType _t : types) {
|
||||
AbstractType t = (AbstractType)_t;
|
||||
typesByPrimary.put(findPrimary(t, types), t);
|
||||
}
|
||||
}
|
||||
private String findPrimary(AbstractType t, YBeanType[] types) {
|
||||
//Note: passing null dynamic context below is okay, assuming the properties in YBeanType
|
||||
// do not care about dynamic context.
|
||||
for (YTypedProperty p : t.getProperties(DynamicSchemaContext.NULL)) {
|
||||
String name = p.getName();
|
||||
if (isUniqueFor(name, t, types)) {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
Assert.isLegal(false, "Couldn't find a unique property key for "+t);
|
||||
return null; //unreachable, but compiler doesn't know.
|
||||
}
|
||||
private boolean isUniqueFor(String name, AbstractType t, YBeanType[] types) {
|
||||
for (YBeanType other : types) {
|
||||
if (other!=t) {
|
||||
//Note: passing null dynamic context below is okay, assuming the properties in YBeanType
|
||||
// do not care about dynamic context.
|
||||
if (other.getPropertiesMap(DynamicSchemaContext.NULL).containsKey(name)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@Override
|
||||
public String toString() {
|
||||
return name;
|
||||
}
|
||||
@Override
|
||||
public boolean isBean() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, YTypedProperty> getPropertiesMap(DynamicSchemaContext dc) {
|
||||
return asMap(getProperties(dc));
|
||||
}
|
||||
|
||||
private Map<String, YTypedProperty> asMap(List<YTypedProperty> properties) {
|
||||
ImmutableMap.Builder<String, YTypedProperty> builder = ImmutableMap.builder();
|
||||
for (YTypedProperty p : properties) {
|
||||
builder.put(p.getName(), p);
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<YTypedProperty> getProperties(DynamicSchemaContext dc) {
|
||||
Set<String> existingProps = dc.getDefinedProperties();
|
||||
if (!existingProps.isEmpty()) {
|
||||
for (Entry<String, AbstractType> entry : typesByPrimary.entrySet()) {
|
||||
String primaryName = entry.getKey();
|
||||
if (existingProps.contains(primaryName)) {
|
||||
return entry.getValue().getProperties(dc);
|
||||
}
|
||||
}
|
||||
}
|
||||
//Reaching here means we couldn't guess the type from existing props.
|
||||
//We'll just return the primary properties, these are good to give as hints
|
||||
//then, since at least one of them should typically be added.
|
||||
return getPrimaryProps(dc);
|
||||
}
|
||||
|
||||
private List<YTypedProperty> getPrimaryProps(DynamicSchemaContext dc) {
|
||||
if (primaryProps==null) {
|
||||
Builder<YTypedProperty> builder = ImmutableList.builder();
|
||||
for (Entry<String, AbstractType> entry : typesByPrimary.entrySet()) {
|
||||
builder.add(entry.getValue().getPropertiesMap(dc).get(entry.getKey()));
|
||||
}
|
||||
primaryProps = builder.build();
|
||||
}
|
||||
return primaryProps;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static class YTypedPropertyImpl implements YTypedProperty {
|
||||
|
||||
@@ -360,4 +539,9 @@ public class YTypeFactory {
|
||||
t.parseWith(new EnumValueParser(name, values));
|
||||
return t;
|
||||
}
|
||||
|
||||
public YValueHint hint(String value, String label) {
|
||||
return new BasicYValueHint(value, label);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -35,6 +35,6 @@ public interface YTypeUtil {
|
||||
ValueParser getValueParser(YType type);
|
||||
|
||||
//TODO: only one of these two should be enough?
|
||||
List<YTypedProperty> getProperties(YType type);
|
||||
Map<String, YTypedProperty> getPropertiesMap(YType yType);
|
||||
List<YTypedProperty> getProperties(YType type, DynamicSchemaContext dc);
|
||||
Map<String, YTypedProperty> getPropertiesMap(YType yType, DynamicSchemaContext dc);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user