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 f698a8201..226a526d2 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 @@ -31,7 +31,13 @@ public class Bean { this.type = type; this.location = location; - this.injectionPoints = injectionPoints; + if (injectionPoints != null && injectionPoints.length == 0) { + this.injectionPoints = DefaultValues.EMPTY_INJECTION_POINTS; + } + else { + this.injectionPoints = injectionPoints; + } + this.supertypes = new HashSet<>(Arrays.asList(supertypes)); } diff --git a/headless-services/commons/commons-lsp-extensions/src/main/java/org/springframework/ide/vscode/commons/protocol/spring/DefaultValues.java b/headless-services/commons/commons-lsp-extensions/src/main/java/org/springframework/ide/vscode/commons/protocol/spring/DefaultValues.java new file mode 100644 index 000000000..40c912747 --- /dev/null +++ b/headless-services/commons/commons-lsp-extensions/src/main/java/org/springframework/ide/vscode/commons/protocol/spring/DefaultValues.java @@ -0,0 +1,17 @@ +/******************************************************************************* + * Copyright (c) 2023 VMware, 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 + * https://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * VMware, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.commons.protocol.spring; + +public class DefaultValues { + + public static InjectionPoint[] EMPTY_INJECTION_POINTS = new InjectionPoint[0]; + +} diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/ASTUtils.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/ASTUtils.java index 64002de26..db724c090 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/ASTUtils.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/ASTUtils.java @@ -47,6 +47,7 @@ import org.slf4j.LoggerFactory; import org.springframework.ide.vscode.boot.java.Annotations; import org.springframework.ide.vscode.boot.java.jdt.imports.ImportRewrite; import org.springframework.ide.vscode.commons.languageserver.completion.DocumentEdits; +import org.springframework.ide.vscode.commons.protocol.spring.DefaultValues; import org.springframework.ide.vscode.commons.protocol.spring.InjectionPoint; import org.springframework.ide.vscode.commons.util.BadLocationException; import org.springframework.ide.vscode.commons.util.CollectorUtil; @@ -60,8 +61,6 @@ public class ASTUtils { private static final Logger log = LoggerFactory.getLogger(ASTUtils.class); - public static InjectionPoint[] EMPTY_INJECTION_POINTS = new InjectionPoint[0]; - public static DocumentRegion nameRegion(TextDocument doc, Annotation annotation) { int start = annotation.getTypeName().getStartPosition(); int end = start + annotation.getTypeName().getLength(); @@ -405,7 +404,7 @@ public class ASTUtils { } } - return result.size() > 0 ? result.toArray(new InjectionPoint[result.size()]) : EMPTY_INJECTION_POINTS; + return result.size() > 0 ? result.toArray(new InjectionPoint[result.size()]) : DefaultValues.EMPTY_INJECTION_POINTS; } public static InjectionPoint[] findInjectionPoints(TypeDeclaration type, TextDocument doc) throws BadLocationException { @@ -454,7 +453,7 @@ public class ASTUtils { } } - return result.size() > 0 ? result.toArray(new InjectionPoint[result.size()]) : EMPTY_INJECTION_POINTS; + return result.size() > 0 ? result.toArray(new InjectionPoint[result.size()]) : DefaultValues.EMPTY_INJECTION_POINTS; } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SymbolCacheOnDisc.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SymbolCacheOnDisc.java index 3afab636d..299909366 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SymbolCacheOnDisc.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SymbolCacheOnDisc.java @@ -30,9 +30,12 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; import org.apache.commons.lang3.tuple.Pair; +import org.eclipse.lsp4j.Location; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.ide.vscode.boot.java.handlers.SymbolAddOnInformation; +import org.springframework.ide.vscode.commons.protocol.spring.Bean; +import org.springframework.ide.vscode.commons.protocol.spring.InjectionPoint; import org.springframework.ide.vscode.commons.util.UriUtil; import com.google.common.collect.ImmutableMultimap; @@ -293,8 +296,11 @@ public class SymbolCacheOnDisc implements SymbolCache { } } - private Gson createGson() { - return new GsonBuilder().registerTypeAdapter(SymbolAddOnInformation.class, new SymbolAddOnInformationAdapter()).create(); + public static Gson createGson() { + return new GsonBuilder() + .registerTypeAdapter(SymbolAddOnInformation.class, new SymbolAddOnInformationAdapter()) + .registerTypeAdapter(Bean.class, new BeanJsonAdapter()) + .create(); } @@ -355,4 +361,29 @@ public class SymbolCacheOnDisc implements SymbolCache { } } + /** + * gson adapter to store subtype information for symbol addon informations + */ + private static class BeanJsonAdapter implements JsonDeserializer { + + @Override + public Bean deserialize(JsonElement json, Type type, JsonDeserializationContext context) throws JsonParseException { + JsonObject parsedObject = json.getAsJsonObject(); + + String beanName = parsedObject.get("name").getAsString(); + String beanType = parsedObject.get("type").getAsString(); + + JsonElement locationObject = parsedObject.get("location"); + Location location = context.deserialize(locationObject, Location.class); + + JsonElement injectionPointObject = parsedObject.get("injectionPoints"); + InjectionPoint[] injectionPoints = context.deserialize(injectionPointObject, InjectionPoint[].class); + + JsonElement supertypesObject = parsedObject.get("supertypes"); + String[] supertypes = context.deserialize(supertypesObject, String[].class); + + return new Bean(beanName, beanType, location, injectionPoints, supertypes); + } + } + } diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/metamodel/test/SpringMetamodelIndexTest.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/metamodel/test/SpringMetamodelIndexTest.java index 0b8b761a2..69360464c 100644 --- a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/metamodel/test/SpringMetamodelIndexTest.java +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/metamodel/test/SpringMetamodelIndexTest.java @@ -14,6 +14,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.List; @@ -24,9 +25,13 @@ import org.eclipse.lsp4j.Position; 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.java.utils.SymbolCacheOnDisc; 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 com.google.gson.Gson; + public class SpringMetamodelIndexTest { private InjectionPoint[] emptyInjectionPoints = new InjectionPoint[0]; @@ -209,5 +214,57 @@ public class SpringMetamodelIndexTest { assertFalse(beansList.contains(bean2)); assertTrue(beansList.contains(bean3)); } + + @Test + void testOverallSerializeDeserializeBeans() { + InjectionPoint point1 = new InjectionPoint("point1", "point1-type", locationForDoc2); + InjectionPoint point2 = new InjectionPoint("point2", "point2-type", locationForDoc1); + + Bean bean1 = new Bean("beanName1", "beanType", locationForDoc1, new InjectionPoint[] {point1, point2}, new String[] {"supertype1", "supertype2"}); + String serialized = bean1.toString(); + + Gson gson = SymbolCacheOnDisc.createGson(); + Bean deserializedBean = gson.fromJson(serialized, Bean.class); + + assertEquals("beanName1", deserializedBean.getName()); + assertEquals("beanType", deserializedBean.getType()); + assertEquals(locationForDoc1, deserializedBean.getLocation()); + + InjectionPoint[] points = deserializedBean.getInjectionPoints(); + assertEquals(2, points.length); + + assertEquals("point1", points[0].getName()); + assertEquals("point1-type", points[0].getType()); + assertEquals(locationForDoc2, points[0].getLocation()); + + assertEquals("point2", points[1].getName()); + assertEquals("point2-type", points[1].getType()); + assertEquals(locationForDoc1, points[1].getLocation()); + + assertTrue(deserializedBean.isTypeCompatibleWith("supertype1")); + assertTrue(deserializedBean.isTypeCompatibleWith("supertype2")); + assertFalse(deserializedBean.isTypeCompatibleWith("java.lang.String")); + } + + @Test + void testEmptyInjectionPointsOptimizationWithSerializeDeserializeBeans() { + Bean bean1 = new Bean("beanName1", "beanType", locationForDoc1, emptyInjectionPoints, emptySupertypes); + String serialized = bean1.toString(); + + Gson gson = SymbolCacheOnDisc.createGson(); + Bean deserializedBean = gson.fromJson(serialized, Bean.class); + + assertEquals("beanName1", deserializedBean.getName()); + assertEquals("beanType", deserializedBean.getType()); + assertEquals(locationForDoc1, deserializedBean.getLocation()); + + assertSame(DefaultValues.EMPTY_INJECTION_POINTS, deserializedBean.getInjectionPoints()); + } + + @Test + void testEmptyInjectionPointsOptimization() { + Bean bean1 = new Bean("beanName1", "beanType", locationForDoc1, emptyInjectionPoints, emptySupertypes); + assertSame(DefaultValues.EMPTY_INJECTION_POINTS, bean1.getInjectionPoints()); + } } diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/metamodel/test/SpringMetamodelIndexerBeansTest.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/metamodel/test/SpringMetamodelIndexerBeansTest.java index 2083e4513..4dc91d532 100644 --- a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/metamodel/test/SpringMetamodelIndexerBeansTest.java +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/metamodel/test/SpringMetamodelIndexerBeansTest.java @@ -33,9 +33,9 @@ import org.springframework.ide.vscode.boot.app.SpringSymbolIndex; import org.springframework.ide.vscode.boot.bootiful.BootLanguageServerTest; import org.springframework.ide.vscode.boot.bootiful.SymbolProviderTestConf; import org.springframework.ide.vscode.boot.index.SpringMetamodelIndex; -import org.springframework.ide.vscode.boot.java.utils.ASTUtils; import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder; 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.project.harness.BootLanguageServerHarness; import org.springframework.ide.vscode.project.harness.ProjectsHarness; @@ -137,7 +137,7 @@ public class SpringMetamodelIndexerBeansTest { InjectionPoint[] injectionPoints = beans[0].getInjectionPoints(); assertEquals(0, injectionPoints.length); - assertSame(ASTUtils.EMPTY_INJECTION_POINTS, injectionPoints); + assertSame(DefaultValues.EMPTY_INJECTION_POINTS, injectionPoints); } @Test @@ -213,7 +213,7 @@ public class SpringMetamodelIndexerBeansTest { InjectionPoint[] injectionPoints = beans[0].getInjectionPoints(); assertEquals(0, injectionPoints.length); - assertSame(ASTUtils.EMPTY_INJECTION_POINTS, injectionPoints); + assertSame(DefaultValues.EMPTY_INJECTION_POINTS, injectionPoints); } @Test