From d6079024920a035ddb6859c9d3838dd88e576aa8 Mon Sep 17 00:00:00 2001 From: Kris De Volder Date: Tue, 8 Aug 2017 13:56:49 -0700 Subject: [PATCH] Basic support for vm_types block in cloudconfig editor --- .../vscode/bosh/BoshCloudConfigSchema.java | 55 +++++++++++++++++-- .../ide/vscode/bosh/BoshSchemas.java | 24 ++++++-- .../ide/vscode/bosh/BoshEditorTest.java | 33 +++++++++++ .../ide/vscode/commons/util/Lazy.java | 30 ++++++++++ 4 files changed, 134 insertions(+), 8 deletions(-) create mode 100644 headless-services/commons/commons-util/src/main/java/org/springframework/ide/vscode/commons/util/Lazy.java diff --git a/headless-services/bosh-language-server/src/main/java/org/springframework/ide/vscode/bosh/BoshCloudConfigSchema.java b/headless-services/bosh-language-server/src/main/java/org/springframework/ide/vscode/bosh/BoshCloudConfigSchema.java index f4ef317d1..f78df5883 100644 --- a/headless-services/bosh-language-server/src/main/java/org/springframework/ide/vscode/bosh/BoshCloudConfigSchema.java +++ b/headless-services/bosh-language-server/src/main/java/org/springframework/ide/vscode/bosh/BoshCloudConfigSchema.java @@ -10,13 +10,23 @@ *******************************************************************************/ package org.springframework.ide.vscode.bosh; +import java.util.Collection; + +import org.apache.commons.lang3.tuple.Pair; import org.springframework.ide.vscode.bosh.models.BoshModels; +import org.springframework.ide.vscode.commons.util.CollectorUtil; import org.springframework.ide.vscode.commons.util.ValueParsers; +import org.springframework.ide.vscode.commons.yaml.reconcile.ASTTypeCache; +import org.springframework.ide.vscode.commons.yaml.reconcile.YamlSchemaProblems; import org.springframework.ide.vscode.commons.yaml.schema.YType; import org.springframework.ide.vscode.commons.yaml.schema.YTypeFactory; import org.springframework.ide.vscode.commons.yaml.schema.YTypeFactory.AbstractType; import org.springframework.ide.vscode.commons.yaml.schema.YTypeFactory.YAtomicType; import org.springframework.ide.vscode.commons.yaml.schema.YTypeFactory.YBeanType; +import org.springframework.ide.vscode.commons.yaml.schema.constraints.Constraints; + +import com.google.common.collect.ImmutableList; + import org.springframework.ide.vscode.commons.yaml.schema.YTypeUtil; import org.springframework.ide.vscode.commons.yaml.schema.YamlSchema; @@ -25,7 +35,10 @@ public class BoshCloudConfigSchema extends SchemaSupport implements YamlSchema { private final YBeanType toplevelType; private final YType t_az_ref; private final YType t_vm_type_ref; + private final YType t_vm_type_def; private final YType t_network_ref; + private Collection definitionTypes; + private Collection> defAndRefTypes; public BoshCloudConfigSchema(YTypeFactory f, BoshModels models) { super(f); @@ -34,14 +47,15 @@ public class BoshCloudConfigSchema extends SchemaSupport implements YamlSchema { AbstractType t_ne_string = f.yatomic("String") .parseWith(ValueParsers.NE_STRING); YAtomicType t_boolean = f.yenum("boolean", "true", "false"); + AbstractType t_pos_integer = f.yatomic("Positive Integer") + .parseWith(ValueParsers.POS_INTEGER); + YType t_params = f.ymap(t_ne_string, t_any); t_az_ref = t_ne_string; t_vm_type_ref = t_ne_string; + t_vm_type_def = f.yatomic("VMTypeName").parseWith(ValueParsers.NE_STRING); t_network_ref = t_ne_string; - AbstractType t_pos_integer = f.yatomic("Positive Integer") - .parseWith(ValueParsers.POS_INTEGER); - YBeanType t_compilation = f.ybean("Compilation"); addProp(t_compilation, "workers", t_pos_integer).isRequired(true); addProp(t_compilation, "reuse_compilation_vms", t_boolean); @@ -49,13 +63,46 @@ public class BoshCloudConfigSchema extends SchemaSupport implements YamlSchema { addProp(t_compilation, "vm_type", t_vm_type_ref).isRequired(true); addProp(t_compilation, "network", t_network_ref).isRequired(true); + YBeanType t_vm_type = f.ybean("VMType"); + addProp(t_vm_type, "name", t_vm_type_def).isPrimary(true); + addProp(t_vm_type, "cloud_properties", t_params); + this.toplevelType = f.ybean("CloudConfig"); addProp(toplevelType, "azs", t_any).isRequired(true); addProp(toplevelType, "networks", t_any).isRequired(true); - addProp(toplevelType, "vm_types", t_any).isRequired(true); + addProp(toplevelType, "vm_types", f.yseq(t_vm_type)).isRequired(true); addProp(toplevelType, "vm_extensions", t_any); addProp(toplevelType, "disk_types", t_any).isRequired(true); addProp(toplevelType, "compilation", t_compilation).isRequired(true); + + ASTTypeCache astTypes = models.astTypes; + for (YType defType : getDefinitionTypes()) { + toplevelType.require(Constraints.uniqueDefinition(astTypes, defType, YamlSchemaProblems.problemType("BOSH_CC_DUPLICATE_"+defType))); + } + + } + + /** + * @return Pairs of types. Each pair contains a 'def' type and a 'ref' type. Nodes with the ref-type + * shall be interpreted as reference to a corresponding node with the 'def' type if the def and ref node + * contain the same scalar value. + */ + public Collection> getDefAndRefTypes() { + if (defAndRefTypes==null) { + defAndRefTypes = ImmutableList.of( + Pair.of(t_vm_type_def, t_vm_type_ref) + ); + } + return defAndRefTypes; + } + + public Collection getDefinitionTypes() { + if (definitionTypes==null) { + definitionTypes = getDefAndRefTypes().stream() + .map(pair -> pair.getLeft()) + .collect(CollectorUtil.toImmutableList()); + } + return definitionTypes; } @Override diff --git a/headless-services/bosh-language-server/src/main/java/org/springframework/ide/vscode/bosh/BoshSchemas.java b/headless-services/bosh-language-server/src/main/java/org/springframework/ide/vscode/bosh/BoshSchemas.java index b5936c47a..8c713b5a9 100644 --- a/headless-services/bosh-language-server/src/main/java/org/springframework/ide/vscode/bosh/BoshSchemas.java +++ b/headless-services/bosh-language-server/src/main/java/org/springframework/ide/vscode/bosh/BoshSchemas.java @@ -14,12 +14,16 @@ import java.util.Collection; import org.apache.commons.lang3.tuple.Pair; import org.springframework.ide.vscode.bosh.models.BoshModels; +import org.springframework.ide.vscode.commons.util.Lazy; import org.springframework.ide.vscode.commons.util.text.LanguageId; import org.springframework.ide.vscode.commons.yaml.schema.YType; import org.springframework.ide.vscode.commons.yaml.schema.YTypeFactory; import org.springframework.ide.vscode.commons.yaml.schema.YTypeUtil; import org.springframework.ide.vscode.commons.yaml.schema.YamlSchema; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableList.Builder; + public class BoshSchemas implements YamlSchema { public final YTypeFactory f = new YTypeFactory() @@ -29,14 +33,16 @@ public class BoshSchemas implements YamlSchema { return deploymentSchema; } + private final Lazy> defTypes = new Lazy<>(); + private final Lazy>> defAndRefTypes = new Lazy<>(); + private final BoshDeploymentManifestSchema deploymentSchema; - private YTypeUtil typeUtil; private final YType toplevelType; private BoshCloudConfigSchema cloudConfigSchema; + public BoshSchemas(BoshModels models) { - this.typeUtil = f.TYPE_UTIL; this.deploymentSchema = new BoshDeploymentManifestSchema(f, models); this.cloudConfigSchema = new BoshCloudConfigSchema(f, models); toplevelType = f.contextAware("BoshSchemas", dc -> { @@ -57,11 +63,21 @@ public class BoshSchemas implements YamlSchema { } public Collection> getDefAndRefTypes() { - return deploymentSchema.getDefAndRefTypes(); + return defAndRefTypes.load(() -> { + Builder> builder = ImmutableList.builder(); + builder.addAll(cloudConfigSchema.getDefAndRefTypes()); + builder.addAll(deploymentSchema.getDefAndRefTypes()); + return builder.build(); + }); } public Collection getDefinitionTypes() { - return deploymentSchema.getDefinitionTypes(); + return defTypes.load(() -> { + Builder builder = ImmutableList.builder(); + builder.addAll(cloudConfigSchema.getDefinitionTypes()); + builder.addAll(deploymentSchema.getDefinitionTypes()); + return builder.build(); + }); } } diff --git a/headless-services/bosh-language-server/src/test/java/org/springframework/ide/vscode/bosh/BoshEditorTest.java b/headless-services/bosh-language-server/src/test/java/org/springframework/ide/vscode/bosh/BoshEditorTest.java index d12cf080c..07bb956c1 100644 --- a/headless-services/bosh-language-server/src/test/java/org/springframework/ide/vscode/bosh/BoshEditorTest.java +++ b/headless-services/bosh-language-server/src/test/java/org/springframework/ide/vscode/bosh/BoshEditorTest.java @@ -1954,4 +1954,37 @@ public class BoshEditorTest { editor.assertHoverContains("network", "References a valid network name defined in the Networks block"); } + @Test public void cloudconfig_vm_types_subproperties() throws Exception { + Editor editor; + + editor = harness.newEditor(LanguageId.BOSH_CLOUD_CONFIG, + "vm_types:\n" + + "- name: nice-vm\n" + + " <*>" + ); + editor.assertContextualCompletions(PLAIN_COMPLETION, "<*>", + "cloud_properties:\n <*>" + ); + + editor = harness.newEditor(LanguageId.BOSH_CLOUD_CONFIG, + "vm_types:\n" + + "- <*>" + ); + editor.assertContextualCompletions("<*>", + "name: <*>" + ); + + editor = harness.newEditor(LanguageId.BOSH_CLOUD_CONFIG, + "vm_types:\n" + + "- name: nice-vm\n" + + "- name: dup\n" + + "- name: dup\n" + ); + editor.ignoreProblem(YamlSchemaProblems.MISSING_PROPERTY); + editor.assertProblems( + "dup|Duplicate 'VMTypeName'", + "dup|Duplicate 'VMTypeName'" + ); + } + } diff --git a/headless-services/commons/commons-util/src/main/java/org/springframework/ide/vscode/commons/util/Lazy.java b/headless-services/commons/commons-util/src/main/java/org/springframework/ide/vscode/commons/util/Lazy.java new file mode 100644 index 000000000..ce06b129f --- /dev/null +++ b/headless-services/commons/commons-util/src/main/java/org/springframework/ide/vscode/commons/util/Lazy.java @@ -0,0 +1,30 @@ +/******************************************************************************* + * Copyright (c) 2017 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.util; + +import java.util.function.Supplier; + +/** + * Utility class to simplify creation of lazy-initialized fields. + */ +public class Lazy { + + private T value; + + public synchronized T load(Supplier loader) { + if (value==null) { + value = loader.get(); + Assert.isNotNull(value); + } + return value; + } + +}