GH-1040: customized gson deserialization of beans in order to invoke constructor instead of gson object creation magic - to benefit from empty injection point optimization code

This commit is contained in:
Martin Lippert
2023-05-20 21:47:35 +02:00
parent 64d4114796
commit b70779f215
6 changed files with 120 additions and 10 deletions

View File

@@ -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));
}

View File

@@ -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];
}

View File

@@ -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;
}

View File

@@ -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<Bean> {
@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);
}
}
}

View File

@@ -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());
}
}

View File

@@ -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