diff --git a/headless-services/commons/commons-lsp-extensions/src/main/java/org/springframework/ide/vscode/commons/RuntimeTypeAdapterFactory.java b/headless-services/commons/commons-lsp-extensions/src/main/java/org/springframework/ide/vscode/commons/RuntimeTypeAdapterFactory.java
index dc0558229..51159d231 100644
--- a/headless-services/commons/commons-lsp-extensions/src/main/java/org/springframework/ide/vscode/commons/RuntimeTypeAdapterFactory.java
+++ b/headless-services/commons/commons-lsp-extensions/src/main/java/org/springframework/ide/vscode/commons/RuntimeTypeAdapterFactory.java
@@ -16,6 +16,11 @@
package org.springframework.ide.vscode.commons;
+import java.io.IOException;
+import java.lang.reflect.AccessFlag;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import com.google.gson.Gson;
import com.google.gson.JsonElement;
@@ -27,39 +32,41 @@ import com.google.gson.TypeAdapterFactory;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
-import java.io.IOException;
-import java.util.LinkedHashMap;
-import java.util.Map;
/**
- * Adapts values whose runtime type may differ from their declaration type. This is necessary when a
- * field's type is not the same type that GSON should create when deserializing that field. For
- * example, consider these types:
+ * Adapts values whose runtime type may differ from their declaration type. This
+ * is necessary when a field's type is not the same type that GSON should create
+ * when deserializing that field. For example, consider these types:
*
*
{@code
* abstract class Shape {
- * int x;
- * int y;
+ * int x;
+ * int y;
* }
+ *
* class Circle extends Shape {
- * int radius;
+ * int radius;
* }
+ *
* class Rectangle extends Shape {
- * int width;
- * int height;
+ * int width;
+ * int height;
* }
+ *
* class Diamond extends Shape {
- * int width;
- * int height;
+ * int width;
+ * int height;
* }
+ *
* class Drawing {
- * Shape bottomShape;
- * Shape topShape;
+ * Shape bottomShape;
+ * Shape topShape;
* }
* }
*
- * Without additional type information, the serialized JSON is ambiguous. Is the bottom shape in
- * this drawing a rectangle or a diamond?
+ *
+ * Without additional type information, the serialized JSON is ambiguous. Is the
+ * bottom shape in this drawing a rectangle or a diamond?
*
*
{@code
* {
@@ -77,8 +84,9 @@ import java.util.Map;
* }
* }
*
- * This class addresses this problem by adding type information to the serialized JSON and honoring
- * that type information when the JSON is deserialized:
+ * This class addresses this problem by adding type information to the
+ * serialized JSON and honoring that type information when the JSON is
+ * deserialized:
*
* {@code
* {
@@ -98,23 +106,22 @@ import java.util.Map;
* }
* }
*
- * Both the type field name ({@code "type"}) and the type labels ({@code "Rectangle"}) are
- * configurable.
+ * Both the type field name ({@code "type"}) and the type labels
+ * ({@code "Rectangle"}) are configurable.
*
* Registering Types
*
- * Create a {@code RuntimeTypeAdapterFactory} by passing the base type and type field name to the
- * {@link #of} factory method. If you don't supply an explicit type field name, {@code "type"} will
- * be used.
+ * Create a {@code RuntimeTypeAdapterFactory} by passing the base type and type
+ * field name to the {@link #of} factory method. If you don't supply an explicit
+ * type field name, {@code "type"} will be used.
*
* {@code
- * RuntimeTypeAdapterFactory shapeAdapterFactory
- * = RuntimeTypeAdapterFactory.of(Shape.class, "type");
+ * RuntimeTypeAdapterFactory shapeAdapterFactory = RuntimeTypeAdapterFactory.of(Shape.class, "type");
* }
*
- * Next register all of your subtypes. Every subtype must be explicitly registered. This protects
- * your application from injection attacks. If you don't supply an explicit type label, the type's
- * simple name will be used.
+ * Next register all of your subtypes. Every subtype must be explicitly
+ * registered. This protects your application from injection attacks. If you
+ * don't supply an explicit type label, the type's simple name will be used.
*
* {@code
* shapeAdapterFactory.registerSubtype(Rectangle.class, "Rectangle");
@@ -122,27 +129,24 @@ import java.util.Map;
* shapeAdapterFactory.registerSubtype(Diamond.class, "Diamond");
* }
*
- * Finally, register the type adapter factory in your application's GSON builder:
+ * Finally, register the type adapter factory in your application's GSON
+ * builder:
*
* {@code
- * Gson gson = new GsonBuilder()
- * .registerTypeAdapterFactory(shapeAdapterFactory)
- * .create();
+ * Gson gson = new GsonBuilder().registerTypeAdapterFactory(shapeAdapterFactory).create();
* }
*
* Like {@code GsonBuilder}, this API supports chaining:
*
* {@code
* RuntimeTypeAdapterFactory shapeAdapterFactory = RuntimeTypeAdapterFactory.of(Shape.class)
- * .registerSubtype(Rectangle.class)
- * .registerSubtype(Circle.class)
- * .registerSubtype(Diamond.class);
+ * .registerSubtype(Rectangle.class).registerSubtype(Circle.class).registerSubtype(Diamond.class);
* }
*
* Serialization and deserialization
*
- * In order to serialize and deserialize a polymorphic object, you must specify the base type
- * explicitly.
+ * In order to serialize and deserialize a polymorphic object, you must specify
+ * the base type explicitly.
*
* {@code
* Diamond diamond = new Diamond();
@@ -156,176 +160,199 @@ import java.util.Map;
* }
*/
public final class RuntimeTypeAdapterFactory implements TypeAdapterFactory {
- private final Class> baseType;
- private final String typeFieldName;
- private final Map> labelToSubtype = new LinkedHashMap<>();
- private final Map, String> subtypeToLabel = new LinkedHashMap<>();
- private final boolean maintainType;
- private boolean recognizeSubtypes;
+ private final Class> baseType;
+ private final String typeFieldName;
+ private final Map> labelToSubtype = new LinkedHashMap<>();
+ private final Map, String> subtypeToLabel = new LinkedHashMap<>();
+ private final boolean maintainType;
+ private boolean recognizeSubtypes;
- private RuntimeTypeAdapterFactory(Class> baseType, String typeFieldName, boolean maintainType) {
- if (typeFieldName == null || baseType == null) {
- throw new NullPointerException();
- }
- this.baseType = baseType;
- this.typeFieldName = typeFieldName;
- this.maintainType = maintainType;
- }
+ private RuntimeTypeAdapterFactory(Class> baseType, String typeFieldName, boolean maintainType) {
+ if (typeFieldName == null || baseType == null) {
+ throw new NullPointerException();
+ }
+ this.baseType = baseType;
+ this.typeFieldName = typeFieldName;
+ this.maintainType = maintainType;
+ }
- /**
- * Creates a new runtime type adapter for {@code baseType} using {@code typeFieldName} as the type
- * field name. Type field names are case sensitive.
- *
- * @param maintainType true if the type field should be included in deserialized objects
- */
- public static RuntimeTypeAdapterFactory of(
- Class baseType, String typeFieldName, boolean maintainType) {
- return new RuntimeTypeAdapterFactory<>(baseType, typeFieldName, maintainType);
- }
+ /**
+ * Creates a new runtime type adapter for {@code baseType} using
+ * {@code typeFieldName} as the type field name. Type field names are case
+ * sensitive.
+ *
+ * @param maintainType true if the type field should be included in deserialized
+ * objects
+ */
+ public static RuntimeTypeAdapterFactory of(Class baseType, String typeFieldName, boolean maintainType) {
+ return new RuntimeTypeAdapterFactory<>(baseType, typeFieldName, maintainType);
+ }
- /**
- * Creates a new runtime type adapter for {@code baseType} using {@code typeFieldName} as the type
- * field name. Type field names are case sensitive.
- */
- public static RuntimeTypeAdapterFactory of(Class baseType, String typeFieldName) {
- return new RuntimeTypeAdapterFactory<>(baseType, typeFieldName, false);
- }
+ /**
+ * Creates a new runtime type adapter for {@code baseType} using
+ * {@code typeFieldName} as the type field name. Type field names are case
+ * sensitive.
+ */
+ public static RuntimeTypeAdapterFactory of(Class baseType, String typeFieldName) {
+ return new RuntimeTypeAdapterFactory<>(baseType, typeFieldName, false);
+ }
- /**
- * Creates a new runtime type adapter for {@code baseType} using {@code "type"} as the type field
- * name.
- */
- public static RuntimeTypeAdapterFactory of(Class baseType) {
- return new RuntimeTypeAdapterFactory<>(baseType, "type", false);
- }
+ /**
+ * Creates a new runtime type adapter for {@code baseType} using {@code "type"}
+ * as the type field name.
+ */
+ public static RuntimeTypeAdapterFactory of(Class baseType) {
+ return new RuntimeTypeAdapterFactory<>(baseType, "type", false);
+ }
- /**
- * Ensures that this factory will handle not just the given {@code baseType}, but any subtype of
- * that type.
- */
- @CanIgnoreReturnValue
- public RuntimeTypeAdapterFactory recognizeSubtypes() {
- this.recognizeSubtypes = true;
- return this;
- }
+ /**
+ * Ensures that this factory will handle not just the given {@code baseType},
+ * but any subtype of that type.
+ */
+ @CanIgnoreReturnValue
+ public RuntimeTypeAdapterFactory recognizeSubtypes() {
+ this.recognizeSubtypes = true;
+ return this;
+ }
+
+ /**
+ * Registers {@code type} identified by {@code label}. Labels are case
+ * sensitive.
+ *
+ * @throws IllegalArgumentException if either {@code type} or {@code label} have
+ * already been registered on this type
+ * adapter.
+ */
+ @CanIgnoreReturnValue
+ public RuntimeTypeAdapterFactory registerSubtype(Class extends T> type, String label) {
+ if (type == null || label == null) {
+ throw new NullPointerException();
+ }
+ if (subtypeToLabel.containsKey(type) || labelToSubtype.containsKey(label)) {
+ throw new IllegalArgumentException("types and labels must be unique");
+ }
+ labelToSubtype.put(label, type);
+ subtypeToLabel.put(type, label);
+ return this;
+ }
- /**
- * Registers {@code type} identified by {@code label}. Labels are case sensitive.
- *
- * @throws IllegalArgumentException if either {@code type} or {@code label} have already been
- * registered on this type adapter.
- */
- @CanIgnoreReturnValue
- public RuntimeTypeAdapterFactory registerSubtype(Class extends T> type, String label) {
- if (type == null || label == null) {
- throw new NullPointerException();
- }
- if (subtypeToLabel.containsKey(type) || labelToSubtype.containsKey(label)) {
- throw new IllegalArgumentException("types and labels must be unique");
- }
- labelToSubtype.put(label, type);
- subtypeToLabel.put(type, label);
- return this;
- }
+ /**
+ * Registers {@code type} identified by its {@link Class#getSimpleName simple
+ * name}. Labels are case sensitive.
+ *
+ * @throws IllegalArgumentException if either {@code type} or its simple name
+ * have already been registered on this type
+ * adapter.
+ */
+ @CanIgnoreReturnValue
+ public RuntimeTypeAdapterFactory registerSubtype(Class extends T> type) {
+ return registerSubtype(type, type.getSimpleName());
+ }
- /**
- * Registers {@code type} identified by its {@link Class#getSimpleName simple name}. Labels are
- * case sensitive.
- *
- * @throws IllegalArgumentException if either {@code type} or its simple name have already been
- * registered on this type adapter.
- */
- @CanIgnoreReturnValue
- public RuntimeTypeAdapterFactory registerSubtype(Class extends T> type) {
- return registerSubtype(type, type.getSimpleName());
- }
+ @Override
+ public TypeAdapter create(Gson gson, TypeToken type) {
+ if (type == null) {
+ return null;
+ }
+ Class> rawType = type.getRawType();
+ boolean handle = recognizeSubtypes ? baseType.isAssignableFrom(rawType) : baseType.equals(rawType);
+ if (!handle) {
+ return null;
+ }
- @Override
- public TypeAdapter create(Gson gson, TypeToken type) {
- if (type == null) {
- return null;
- }
- Class> rawType = type.getRawType();
- boolean handle =
- recognizeSubtypes ? baseType.isAssignableFrom(rawType) : baseType.equals(rawType);
- if (!handle) {
- return null;
- }
+ TypeAdapter jsonElementAdapter = gson.getAdapter(JsonElement.class);
+ Map> labelToDelegate = new LinkedHashMap<>();
+ Map, TypeAdapter>> subtypeToDelegate = new LinkedHashMap<>();
+ for (Map.Entry> entry : labelToSubtype.entrySet()) {
+ TypeAdapter> delegate = gson.getDelegateAdapter(this, TypeToken.get(entry.getValue()));
+ labelToDelegate.put(entry.getKey(), delegate);
+ subtypeToDelegate.put(entry.getValue(), delegate);
+ }
+
+ final RuntimeTypeAdapterFactory thisAdapter = this;
+
+ return new TypeAdapter() {
- TypeAdapter jsonElementAdapter = gson.getAdapter(JsonElement.class);
- Map> labelToDelegate = new LinkedHashMap<>();
- Map, TypeAdapter>> subtypeToDelegate = new LinkedHashMap<>();
- for (Map.Entry> entry : labelToSubtype.entrySet()) {
- TypeAdapter> delegate = gson.getDelegateAdapter(this, TypeToken.get(entry.getValue()));
- labelToDelegate.put(entry.getKey(), delegate);
- subtypeToDelegate.put(entry.getValue(), delegate);
- }
+ private TypeAdapter> findSubtypeDelegate(Class> clazz) {
+ if (clazz.isInterface() || clazz.accessFlags().contains(AccessFlag.ABSTRACT)) {
+ throw new JsonParseException(
+ "cannot serialize/deserialize " + clazz.getName() + "; it is abstract");
+ }
+ if (baseType.isAssignableFrom(clazz)) {
+ TypeAdapter> delegate = gson.getDelegateAdapter(thisAdapter, TypeToken.get(clazz));
+ labelToDelegate.put(clazz.getName(), delegate);
+ subtypeToDelegate.put(clazz, delegate);
+ labelToSubtype.put(clazz.getName(), clazz);
+ subtypeToLabel.put(clazz, clazz.getName());
+ return delegate;
+ }
+ return null;
+ }
- return new TypeAdapter() {
- @Override
- public R read(JsonReader in) throws IOException {
- JsonElement jsonElement = jsonElementAdapter.read(in);
- JsonElement labelJsonElement;
- if (maintainType) {
- labelJsonElement = jsonElement.getAsJsonObject().get(typeFieldName);
- } else {
- labelJsonElement = jsonElement.getAsJsonObject().remove(typeFieldName);
- }
+ @SuppressWarnings("unchecked") // registration requires that subtype extends T
+ @Override
+ public R read(JsonReader in) throws IOException {
+ JsonElement jsonElement = jsonElementAdapter.read(in);
+ JsonElement labelJsonElement;
+ if (maintainType) {
+ labelJsonElement = jsonElement.getAsJsonObject().get(typeFieldName);
+ } else {
+ labelJsonElement = jsonElement.getAsJsonObject().remove(typeFieldName);
+ }
- if (labelJsonElement == null) {
- throw new JsonParseException(
- "cannot deserialize "
- + baseType
- + " because it does not define a field named "
- + typeFieldName);
- }
- String label = labelJsonElement.getAsString();
- @SuppressWarnings("unchecked") // registration requires that subtype extends T
- TypeAdapter delegate = (TypeAdapter) labelToDelegate.get(label);
- if (delegate == null) {
- throw new JsonParseException(
- "cannot deserialize "
- + baseType
- + " subtype named "
- + label
- + "; did you forget to register a subtype?");
- }
- return delegate.fromJsonTree(jsonElement);
- }
+ if (labelJsonElement == null) {
+ throw new JsonParseException("cannot deserialize " + baseType
+ + " because it does not define a field named " + typeFieldName);
+ }
+ String label = labelJsonElement.getAsString();
+ TypeAdapter delegate = (TypeAdapter) labelToDelegate.get(label);
+ if (delegate == null && recognizeSubtypes) {
+ try {
+ delegate = (TypeAdapter) findSubtypeDelegate(Class.forName(label));
+ } catch (ClassNotFoundException e) {
+ // ignore
+ }
+ }
+ if (delegate == null) {
+ throw new JsonParseException("cannot deserialize " + baseType + " subtype named " + label
+ + "; did you forget to register a subtype?");
+ }
+ return delegate.fromJsonTree(jsonElement);
+ }
- @Override
- public void write(JsonWriter out, R value) throws IOException {
- Class> srcType = value.getClass();
- String label = subtypeToLabel.get(srcType);
- @SuppressWarnings("unchecked") // registration requires that subtype extends T
- TypeAdapter delegate = (TypeAdapter) subtypeToDelegate.get(srcType);
- if (delegate == null) {
- throw new JsonParseException(
- "cannot serialize " + srcType.getName() + "; did you forget to register a subtype?");
- }
- JsonObject jsonObject = delegate.toJsonTree(value).getAsJsonObject();
+ @SuppressWarnings("unchecked") // registration requires that subtype extends T
+ @Override
+ public void write(JsonWriter out, R value) throws IOException {
+ Class> srcType = value.getClass();
+ TypeAdapter delegate = (TypeAdapter) subtypeToDelegate.get(srcType);
+ if (delegate == null && recognizeSubtypes) {
+ delegate = (TypeAdapter) findSubtypeDelegate(srcType);
+ }
+ if (delegate == null) {
+ throw new JsonParseException(
+ "cannot serialize " + srcType.getName() + "; did you forget to register a subtype?");
+ }
+ JsonObject jsonObject = delegate.toJsonTree(value).getAsJsonObject();
- if (maintainType) {
- jsonElementAdapter.write(out, jsonObject);
- return;
- }
+ if (maintainType) {
+ jsonElementAdapter.write(out, jsonObject);
+ return;
+ }
- JsonObject clone = new JsonObject();
+ JsonObject clone = new JsonObject();
- if (jsonObject.has(typeFieldName)) {
- throw new JsonParseException(
- "cannot serialize "
- + srcType.getName()
- + " because it already defines a field named "
- + typeFieldName);
- }
- clone.add(typeFieldName, new JsonPrimitive(label));
+ if (jsonObject.has(typeFieldName)) {
+ throw new JsonParseException("cannot serialize " + srcType.getName()
+ + " because it already defines a field named " + typeFieldName);
+ }
+ String label = subtypeToLabel.get(srcType);
+ clone.add(typeFieldName, new JsonPrimitive(label));
- for (Map.Entry e : jsonObject.entrySet()) {
- clone.add(e.getKey(), e.getValue());
- }
- jsonElementAdapter.write(out, clone);
- }
- }.nullSafe();
- }
+ for (Map.Entry e : jsonObject.entrySet()) {
+ clone.add(e.getKey(), e.getValue());
+ }
+ jsonElementAdapter.write(out, clone);
+ }
+ }.nullSafe();
+ }
}
\ No newline at end of file
diff --git a/headless-services/commons/commons-lsp-extensions/src/main/java/org/springframework/ide/vscode/commons/protocol/spring/Bean.java b/headless-services/commons/commons-lsp-extensions/src/main/java/org/springframework/ide/vscode/commons/protocol/spring/Bean.java
index e3ba120eb..fd18880cd 100644
--- a/headless-services/commons/commons-lsp-extensions/src/main/java/org/springframework/ide/vscode/commons/protocol/spring/Bean.java
+++ b/headless-services/commons/commons-lsp-extensions/src/main/java/org/springframework/ide/vscode/commons/protocol/spring/Bean.java
@@ -46,24 +46,21 @@ public class Bean extends AbstractSpringIndexElement implements SymbolElement {
this.symbolLabel = symbolLabel;
if (injectionPoints != null && injectionPoints.length == 0) {
- this.injectionPoints = DefaultValues.EMPTY_INJECTION_POINTS;
+ this.injectionPoints = null;
}
else {
this.injectionPoints = injectionPoints;
}
if (supertypes != null && supertypes.size() == 0) {
- this.supertypes = DefaultValues.EMPTY_SUPERTYPES;
- }
- else if (supertypes != null && supertypes.size() == 1 && supertypes.contains("java.lang.Object")) {
- this.supertypes = DefaultValues.OBJECT_SUPERTYPE;
+ this.supertypes = null;
}
else {
this.supertypes = supertypes;
}
if (annotations != null && annotations.length == 0) {
- this.annotations = DefaultValues.EMPTY_ANNOTATIONS;
+ this.annotations = null;
}
else {
this.annotations = annotations;
@@ -83,15 +80,15 @@ public class Bean extends AbstractSpringIndexElement implements SymbolElement {
}
public InjectionPoint[] getInjectionPoints() {
- return injectionPoints;
+ return injectionPoints == null ? DefaultValues.EMPTY_INJECTION_POINTS : injectionPoints;
}
public boolean isTypeCompatibleWith(String type) {
- return type != null && ((this.type != null && this.type.equals(type)) || (supertypes.contains(type)));
+ return type != null && ((this.type != null && this.type.equals(type)) || (getSupertypes().contains(type)));
}
public AnnotationMetadata[] getAnnotations() {
- return annotations;
+ return annotations == null ? DefaultValues.EMPTY_ANNOTATIONS : annotations;
}
public boolean isConfiguration() {
@@ -99,7 +96,7 @@ public class Bean extends AbstractSpringIndexElement implements SymbolElement {
}
public Set getSupertypes() {
- return supertypes;
+ return supertypes == null ? DefaultValues.EMPTY_SUPERTYPES : supertypes;
}
public String getSymbolLabel() {
diff --git a/headless-services/commons/commons-lsp-extensions/src/main/java/org/springframework/ide/vscode/commons/protocol/spring/InjectionPoint.java b/headless-services/commons/commons-lsp-extensions/src/main/java/org/springframework/ide/vscode/commons/protocol/spring/InjectionPoint.java
index 57598faab..b3794d0e8 100644
--- a/headless-services/commons/commons-lsp-extensions/src/main/java/org/springframework/ide/vscode/commons/protocol/spring/InjectionPoint.java
+++ b/headless-services/commons/commons-lsp-extensions/src/main/java/org/springframework/ide/vscode/commons/protocol/spring/InjectionPoint.java
@@ -29,8 +29,8 @@ public class InjectionPoint {
this.type = type;
this.location = location;
- if (annotations == null || (annotations != null && annotations.length == 0)) {
- this.annotations = DefaultValues.EMPTY_ANNOTATIONS;
+ if (annotations != null && annotations.length == 0) {
+ this.annotations = null;
}
else {
this.annotations = annotations;
@@ -50,7 +50,7 @@ public class InjectionPoint {
}
public AnnotationMetadata[] getAnnotations() {
- return annotations;
+ return annotations == null ? DefaultValues.EMPTY_ANNOTATIONS : annotations;
}
}
diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootLanguageServerBootApp.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootLanguageServerBootApp.java
index eb083d24f..d281462c2 100644
--- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootLanguageServerBootApp.java
+++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootLanguageServerBootApp.java
@@ -46,7 +46,6 @@ import org.springframework.ide.vscode.boot.index.cache.IndexCache;
import org.springframework.ide.vscode.boot.index.cache.IndexCacheOnDiscDeltaBased;
import org.springframework.ide.vscode.boot.index.cache.IndexCacheVoid;
import org.springframework.ide.vscode.boot.java.JavaDefinitionHandler;
-import org.springframework.ide.vscode.boot.java.beans.ConfigPropertyIndexElement;
import org.springframework.ide.vscode.boot.java.beans.DependsOnDefinitionProvider;
import org.springframework.ide.vscode.boot.java.beans.NamedDefinitionProvider;
import org.springframework.ide.vscode.boot.java.beans.QualifierDefinitionProvider;
@@ -54,11 +53,8 @@ import org.springframework.ide.vscode.boot.java.beans.ResourceDefinitionProvider
import org.springframework.ide.vscode.boot.java.conditionals.ConditionalOnBeanDefinitionProvider;
import org.springframework.ide.vscode.boot.java.conditionals.ConditionalOnResourceDefinitionProvider;
import org.springframework.ide.vscode.boot.java.copilot.util.ResponseModifier;
-import org.springframework.ide.vscode.boot.java.data.QueryMethodIndexElement;
import org.springframework.ide.vscode.boot.java.data.jpa.queries.DataQueryParameterDefinitionProvider;
import org.springframework.ide.vscode.boot.java.data.jpa.queries.JdtDataQuerySemanticTokensProvider;
-import org.springframework.ide.vscode.boot.java.events.EventListenerIndexElement;
-import org.springframework.ide.vscode.boot.java.events.EventPublisherIndexElement;
import org.springframework.ide.vscode.boot.java.handlers.BootJavaCodeActionProvider;
import org.springframework.ide.vscode.boot.java.handlers.BootJavaReconcileEngine;
import org.springframework.ide.vscode.boot.java.handlers.JavaCodeActionHandler;
@@ -78,8 +74,6 @@ import org.springframework.ide.vscode.boot.java.livehover.v2.SpringProcessLiveDa
import org.springframework.ide.vscode.boot.java.reconcilers.JavaReconciler;
import org.springframework.ide.vscode.boot.java.reconcilers.JdtAstReconciler;
import org.springframework.ide.vscode.boot.java.reconcilers.JdtReconciler;
-import org.springframework.ide.vscode.boot.java.requestmapping.RequestMappingIndexElement;
-import org.springframework.ide.vscode.boot.java.requestmapping.WebfluxRouteElementRangesIndexElement;
import org.springframework.ide.vscode.boot.java.spel.SpelDefinitionProvider;
import org.springframework.ide.vscode.boot.java.utils.CompilationUnitCache;
import org.springframework.ide.vscode.boot.java.value.ValueDefinitionProvider;
@@ -106,11 +100,6 @@ import org.springframework.ide.vscode.commons.languageserver.util.LanguageComput
import org.springframework.ide.vscode.commons.languageserver.util.LspClient;
import org.springframework.ide.vscode.commons.languageserver.util.ServerCapabilityInitializer;
import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer;
-import org.springframework.ide.vscode.commons.protocol.spring.AbstractSpringIndexElement;
-import org.springframework.ide.vscode.commons.protocol.spring.AotProcessorElement;
-import org.springframework.ide.vscode.commons.protocol.spring.BeanMethodContainerElement;
-import org.springframework.ide.vscode.commons.protocol.spring.DocumentElement;
-import org.springframework.ide.vscode.commons.protocol.spring.ProjectElement;
import org.springframework.ide.vscode.commons.protocol.spring.SpringIndexElement;
import org.springframework.ide.vscode.commons.util.FileObserver;
import org.springframework.ide.vscode.commons.util.LogRedirect;
@@ -447,18 +436,6 @@ public class BootLanguageServerBootApp {
Consumer configureGson() {
return builder -> builder
.registerTypeAdapterFactory(RuntimeTypeAdapterFactory.of(SpringIndexElement.class, "_internal_node_type")
- .recognizeSubtypes()
- .registerSubtype(org.springframework.ide.vscode.commons.protocol.spring.Bean.class)
- .registerSubtype(AotProcessorElement.class)
- .registerSubtype(BeanMethodContainerElement.class)
- .registerSubtype(ConfigPropertyIndexElement.class)
- .registerSubtype(DocumentElement.class)
- .registerSubtype(EventListenerIndexElement.class)
- .registerSubtype(EventPublisherIndexElement.class)
- .registerSubtype(ProjectElement.class)
- .registerSubtype(QueryMethodIndexElement.class)
- .registerSubtype(RequestMappingIndexElement.class)
- .registerSubtype(WebfluxRouteElementRangesIndexElement.class)
- .registerSubtype(AbstractSpringIndexElement.class));
+ .recognizeSubtypes());
}
}
diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/index/cache/IndexCacheOnDiscDeltaBased.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/index/cache/IndexCacheOnDiscDeltaBased.java
index 8da2a2aaa..433439145 100644
--- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/index/cache/IndexCacheOnDiscDeltaBased.java
+++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/index/cache/IndexCacheOnDiscDeltaBased.java
@@ -40,19 +40,7 @@ import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.springframework.ide.vscode.boot.java.beans.ConfigPropertyIndexElement;
-import org.springframework.ide.vscode.boot.java.data.QueryMethodIndexElement;
-import org.springframework.ide.vscode.boot.java.events.EventListenerIndexElement;
-import org.springframework.ide.vscode.boot.java.events.EventPublisherIndexElement;
-import org.springframework.ide.vscode.boot.java.requestmapping.RequestMappingIndexElement;
-import org.springframework.ide.vscode.boot.java.requestmapping.WebfluxRouteElementRangesIndexElement;
import org.springframework.ide.vscode.commons.RuntimeTypeAdapterFactory;
-import org.springframework.ide.vscode.commons.protocol.spring.AbstractSpringIndexElement;
-import org.springframework.ide.vscode.commons.protocol.spring.AotProcessorElement;
-import org.springframework.ide.vscode.commons.protocol.spring.Bean;
-import org.springframework.ide.vscode.commons.protocol.spring.BeanMethodContainerElement;
-import org.springframework.ide.vscode.commons.protocol.spring.DocumentElement;
-import org.springframework.ide.vscode.commons.protocol.spring.ProjectElement;
import org.springframework.ide.vscode.commons.protocol.spring.SpringIndexElement;
import org.springframework.ide.vscode.commons.util.UriUtil;
@@ -579,21 +567,7 @@ public class IndexCacheOnDiscDeltaBased implements IndexCache {
.registerTypeAdapter(DeltaStorage.class, new DeltaStorageAdapter())
.registerTypeAdapter(IndexCacheStore.class, new IndexCacheStoreAdapter())
.registerTypeAdapterFactory(RuntimeTypeAdapterFactory.of(SpringIndexElement.class, "_internal_node_type")
- .recognizeSubtypes()
- .registerSubtype(Bean.class)
- .registerSubtype(AotProcessorElement.class)
- .registerSubtype(BeanMethodContainerElement.class)
- .registerSubtype(ConfigPropertyIndexElement.class)
- .registerSubtype(DocumentElement.class)
- .registerSubtype(EventListenerIndexElement.class)
- .registerSubtype(EventPublisherIndexElement.class)
- .registerSubtype(ProjectElement.class)
- .registerSubtype(QueryMethodIndexElement.class)
- .registerSubtype(RequestMappingIndexElement.class)
- .registerSubtype(WebfluxRouteElementRangesIndexElement.class)
- .registerSubtype(AbstractSpringIndexElement.class))
-
-
+ .recognizeSubtypes())
.create();
}
diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/index/test/SpringMetamodelIndexTest.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/index/test/SpringMetamodelIndexTest.java
index ae3740a8b..26c37b6ec 100644
--- a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/index/test/SpringMetamodelIndexTest.java
+++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/index/test/SpringMetamodelIndexTest.java
@@ -29,14 +29,12 @@ import org.eclipse.lsp4j.Range;
import org.junit.jupiter.api.Test;
import org.springframework.ide.vscode.boot.index.SpringMetamodelIndex;
import org.springframework.ide.vscode.boot.index.cache.IndexCacheOnDiscDeltaBased;
-import org.springframework.ide.vscode.boot.java.beans.ConfigPropertyIndexElement;
-import org.springframework.ide.vscode.boot.java.data.QueryMethodIndexElement;
+import org.springframework.ide.vscode.commons.protocol.spring.AbstractSpringIndexElement;
import org.springframework.ide.vscode.commons.protocol.spring.AnnotationAttributeValue;
import org.springframework.ide.vscode.commons.protocol.spring.AnnotationMetadata;
import org.springframework.ide.vscode.commons.protocol.spring.Bean;
import org.springframework.ide.vscode.commons.protocol.spring.DefaultValues;
import org.springframework.ide.vscode.commons.protocol.spring.InjectionPoint;
-import org.springframework.ide.vscode.commons.protocol.spring.ProjectElement;
import org.springframework.ide.vscode.commons.protocol.spring.SpringIndexElement;
import com.google.gson.Gson;
@@ -271,7 +269,7 @@ public class SpringMetamodelIndexTest {
assertEquals("point2", points[1].getName());
assertEquals("point2-type", points[1].getType());
assertEquals(locationForDoc1, points[1].getLocation());
- assertEquals(0, points[1].getAnnotations().length);
+ assertSame(DefaultValues.EMPTY_ANNOTATIONS, points[1].getAnnotations());
assertTrue(deserializedBean.isTypeCompatibleWith("supertype1"));
assertTrue(deserializedBean.isTypeCompatibleWith("supertype2"));
@@ -311,7 +309,7 @@ public class SpringMetamodelIndexTest {
assertEquals("beanType", deserializedBean.getType());
assertEquals(locationForDoc1, deserializedBean.getLocation());
- assertEquals(0, deserializedBean.getInjectionPoints().length);
+ assertSame(DefaultValues.EMPTY_INJECTION_POINTS, deserializedBean.getInjectionPoints());
}
@Test
@@ -403,7 +401,7 @@ public class SpringMetamodelIndexTest {
void testBasicSpringIndexStructure() {
Bean bean1 = new Bean("beanName1", "beanType1", locationForDoc1, emptyInjectionPoints, Set.of("supertype1", "supertype2"), emptyAnnotations, false, "symbolLabel");
- ConfigPropertyIndexElement child1 = new ConfigPropertyIndexElement("prop1", "java.lang.String", null);
+ SubType1 child1 = new SubType1();
bean1.addChild(child1);
List children2 = bean1.getChildren();
@@ -415,32 +413,32 @@ public class SpringMetamodelIndexTest {
void testSpringIndexStructurePolymorphicSerialization() {
Gson gson = IndexCacheOnDiscDeltaBased.createGson();
- QueryMethodIndexElement subNode = new QueryMethodIndexElement("find1", "SELECT * FROM All", null);
+ SubType2 subNode = new SubType2();
- ConfigPropertyIndexElement node1 = new ConfigPropertyIndexElement("prop1", "java.lang.String", null);
+ SubType1 node1 = new SubType1();
node1.addChild(subNode);
- QueryMethodIndexElement node2 = new QueryMethodIndexElement("find", "SELECT * FROM S", null);
+ SubType2 node2 = new SubType2();
- ProjectElement root = new ProjectElement("my-project");
+ Root root = new Root();
root.addChild(node1);
root.addChild(node2);
String json = gson.toJson(root);
- ProjectElement deserializedRoot = gson.fromJson(json, ProjectElement.class);
+ Root deserializedRoot = gson.fromJson(json, Root.class);
List children = deserializedRoot.getChildren();
assertEquals(2, children.size());
- ConfigPropertyIndexElement deserializedNode1 = (ConfigPropertyIndexElement) children.stream().filter(node -> node instanceof ConfigPropertyIndexElement).findAny().get();
- QueryMethodIndexElement deserializedNode2 = (QueryMethodIndexElement) children.stream().filter(node -> node instanceof QueryMethodIndexElement).findAny().get();
+ SubType1 deserializedNode1 = (SubType1) children.stream().filter(node -> node instanceof SubType1).findAny().get();
+ SubType2 deserializedNode2 = (SubType2) children.stream().filter(node -> node instanceof SubType2).findAny().get();
assertNotNull(deserializedNode1);
assertNotNull(deserializedNode2);
List deserializedChild2 = deserializedNode1.getChildren();
assertEquals(1, deserializedChild2.size());
- assertTrue(deserializedChild2.get(0) instanceof QueryMethodIndexElement);
+ assertTrue(deserializedChild2.get(0) instanceof SubType2);
}
@Test
@@ -472,11 +470,11 @@ public class SpringMetamodelIndexTest {
Gson gson = IndexCacheOnDiscDeltaBased.createGson();
- QueryMethodIndexElement childOfChild = new QueryMethodIndexElement("find1", "SELECT * FROM All", null);
- ConfigPropertyIndexElement child1 = new ConfigPropertyIndexElement("prop1", "java.lang.String", null);
+ SubType2 childOfChild = new SubType2();
+ SubType1 child1 = new SubType1();
child1.addChild(childOfChild);
- QueryMethodIndexElement child2 = new QueryMethodIndexElement("find2", "SELECT s2 FROM S", null);
+ SubType2 child2 = new SubType2();
Bean bean1 = new Bean("beanName1", "beanType", locationForDoc1, emptyInjectionPoints, emptySupertypes, emptyAnnotations, true, "symbolLabel");
bean1.addChild(child1);
bean1.addChild(child2);
@@ -487,14 +485,14 @@ public class SpringMetamodelIndexTest {
List children = deserializedBean.getChildren();
assertEquals(2, children.size());
- SpringIndexElement deserializedChild1 = children.stream().filter(element -> element instanceof ConfigPropertyIndexElement).findAny().get();
+ SpringIndexElement deserializedChild1 = children.stream().filter(element -> element instanceof SubType1).findAny().get();
assertNotNull(deserializedChild1);
List childrenOfChild = deserializedChild1.getChildren();
assertEquals(1, childrenOfChild.size());
- assertTrue(childrenOfChild.get(0) instanceof QueryMethodIndexElement);
+ assertTrue(childrenOfChild.get(0) instanceof SubType2);
- SpringIndexElement deserializedChild2 = children.stream().filter(element -> element instanceof QueryMethodIndexElement).findAny().get();
+ SpringIndexElement deserializedChild2 = children.stream().filter(element -> element instanceof SubType2).findAny().get();
assertNotNull(deserializedChild2);
assertEquals(0, deserializedChild2.getChildren().size());
}
@@ -504,18 +502,27 @@ public class SpringMetamodelIndexTest {
Gson gson = IndexCacheOnDiscDeltaBased.createGson();
- ConfigPropertyIndexElement child1 = new ConfigPropertyIndexElement("prop1", "java.lang.String", null);;
+ SubType1 child1 = new SubType1();
Bean bean1 = new Bean("beanName1", "beanType", locationForDoc1, emptyInjectionPoints, emptySupertypes, emptyAnnotations, true, "symbolLabel");
bean1.addChild(child1);
String serialized = gson.toJson(bean1);
Bean deserializedBean = gson.fromJson(serialized, Bean.class);
- QueryMethodIndexElement newChild = new QueryMethodIndexElement("find1", "SELECT * FROM All", null);
+ SubType2 newChild = new SubType2();
deserializedBean.addChild(newChild);
List childrenAfterNewChildAdded = deserializedBean.getChildren();
assertEquals(2, childrenAfterNewChildAdded.size());
}
+ static class SubType1 extends AbstractSpringIndexElement {
+ }
+
+ static class SubType2 extends AbstractSpringIndexElement {
+ }
+
+ static class Root extends AbstractSpringIndexElement {
+ }
+
}
diff --git a/vscode-extensions/vscode-spring-boot/lib/explorer/structure-tree-manager.ts b/vscode-extensions/vscode-spring-boot/lib/explorer/structure-tree-manager.ts
index f9c41be4e..2348c9d0b 100644
--- a/vscode-extensions/vscode-spring-boot/lib/explorer/structure-tree-manager.ts
+++ b/vscode-extensions/vscode-spring-boot/lib/explorer/structure-tree-manager.ts
@@ -23,11 +23,11 @@ export class StructureManager {
private parseNode(json: any): SpringNode | undefined {
if (typeof (json._internal_node_type) === 'string') {
switch (json._internal_node_type) {
- case "ProjectElement":
+ case "org.springframework.ide.vscode.commons.protocol.spring.ProjectElement":
return new ProjectNode(json.projectName as string, this.parseArray(json.children));
- case "DocumentElement":
+ case "org.springframework.ide.vscode.commons.protocol.spring.DocumentElement":
return new DocumentNode(Uri.parse(json.docURI as string), this.parseArray(json.children));
- case "Bean":
+ case "org.springframework.ide.vscode.commons.protocol.spring.Bean":
return new BeanNode(
this.parseArray(json.children),
json.name,
@@ -39,19 +39,19 @@ export class StructureManager {
json.isConfiguration,
json.symbolLabel
);
- case "AotProcessorElement":
+ case "org.springframework.ide.vscode.commons.protocol.spring.AotProcessorElement":
return new AotProcessorNode(
this.parseArray(json.children),
json.name,
Uri.parse(json.docUri)
);
- case "BeanMethodContainerElement":
+ case "org.springframework.ide.vscode.commons.protocol.spring.BeanMethodContainerElement":
return new BeanMethodContainerNode(
this.parseArray(json.children),
json.type,
json.location
);
- case "BeanRegistrarElement":
+ case "org.springframework.ide.vscode.commons.protocol.spring.BeanRegistrarElement":
return new BeanRegistrarNode(
this.parseArray(json.children),
json.name,
@@ -59,14 +59,14 @@ export class StructureManager {
json.location
);
- case "ConfigPropertyIndexElement":
+ case "org.springframework.ide.vscode.boot.java.beans.ConfigPropertyIndexElement":
return new ConfigPropertyNode(
this.parseArray(json.children),
json.name,
json.type,
json.range
);
- case "EventListenerIndexElement":
+ case "org.springframework.ide.vscode.boot.java.events.EventListenerIndexElement":
return new EventListenerNode(
this.parseArray(json.children),
json.eventType,
@@ -74,21 +74,21 @@ export class StructureManager {
json.containerBeanType,
json.annotations
);
- case "EventPublisherIndexElement":
+ case "org.springframework.ide.vscode.boot.java.events.EventPublisherIndexElement":
return new EventPublisherNode(
this.parseArray(json.children),
json.eventType,
json.location,
json.eventTypesFromHierarchy,
);
- case "QueryMethodIndexElement":
+ case "org.springframework.ide.vscode.boot.java.data.QueryMethodIndexElement":
return new QueryMethodNode(
this.parseArray(json.children),
json.methodName,
json.queryString,
json.range
);
- case "RequestMappingIndexElement":
+ case "org.springframework.ide.vscode.boot.java.requestmapping.RequestMappingIndexElement":
return new RequestMappingNode(
this.parseArray(json.children),
json.path,
@@ -98,7 +98,7 @@ export class StructureManager {
json.symbolLabel,
json.range
);
- case "WebfluxRouteElementRangesIndexElement":
+ case "org.springframework.ide.vscode.boot.java.requestmapping.WebfluxRouteElementRangesIndexElement":
return new WebfluxRoutesNode(
this.parseArray(json.children),
json.path,