GH-1431: replaced scan pass logic with exception handling and removed AbstractSymbolProvider
GH-1431: changed indexing of components to index embedded components like bean methods, request mappings, etc. GH-1431: added new tests for index structure Signed-off-by: Martin Lippert <martin.lippert@broadcom.com>
This commit is contained in:
@@ -0,0 +1,166 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017, 2025 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
|
||||
* https://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.beans;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jdt.core.dom.ASTNode;
|
||||
import org.eclipse.jdt.core.dom.Annotation;
|
||||
import org.eclipse.jdt.core.dom.IAnnotationBinding;
|
||||
import org.eclipse.jdt.core.dom.ITypeBinding;
|
||||
import org.eclipse.jdt.core.dom.MethodDeclaration;
|
||||
import org.eclipse.jdt.core.dom.Modifier;
|
||||
import org.eclipse.jdt.core.dom.ParameterizedType;
|
||||
import org.eclipse.jdt.core.dom.Type;
|
||||
import org.eclipse.lsp4j.Location;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.ide.vscode.boot.java.Annotations;
|
||||
import org.springframework.ide.vscode.boot.java.reconcilers.RequiredCompleteAstException;
|
||||
import org.springframework.ide.vscode.boot.java.requestmapping.WebfluxRouterSymbolProvider;
|
||||
import org.springframework.ide.vscode.boot.java.utils.ASTUtils;
|
||||
import org.springframework.ide.vscode.boot.java.utils.FunctionUtils;
|
||||
import org.springframework.ide.vscode.boot.java.utils.SpringIndexerJavaContext;
|
||||
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.InjectionPoint;
|
||||
import org.springframework.ide.vscode.commons.util.BadLocationException;
|
||||
import org.springframework.ide.vscode.commons.util.text.DocumentRegion;
|
||||
import org.springframework.ide.vscode.commons.util.text.TextDocument;
|
||||
|
||||
import reactor.util.function.Tuple2;
|
||||
|
||||
/**
|
||||
* @author Martin Lippert
|
||||
* @author Kris De Volder
|
||||
*/
|
||||
public class BeansIndexer {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(BeansIndexer.class);
|
||||
|
||||
public static void indexBeanMethod(Bean configBean, Annotation node, SpringIndexerJavaContext context, TextDocument doc) {
|
||||
if (node == null) return;
|
||||
|
||||
ASTNode parent = node.getParent();
|
||||
if (parent == null || !(parent instanceof MethodDeclaration)) return;
|
||||
|
||||
MethodDeclaration method = (MethodDeclaration) parent;
|
||||
if (isMethodAbstract(method)) return;
|
||||
|
||||
boolean isWebfluxRouter = WebfluxRouterSymbolProvider.isWebfluxRouterBean(method);
|
||||
if (isWebfluxRouter) {
|
||||
if (!context.isFullAst()) {
|
||||
throw new RequiredCompleteAstException();
|
||||
}
|
||||
}
|
||||
|
||||
boolean isFunction = isFunctionBean(method);
|
||||
ITypeBinding beanType = getBeanType(method);
|
||||
String markerString = getAnnotations(method);
|
||||
|
||||
for (Tuple2<String, DocumentRegion> nameAndRegion : BeanUtils.getBeanNamesFromBeanAnnotationWithRegions(node, doc)) {
|
||||
try {
|
||||
Location location = new Location(doc.getUri(), doc.toRange(nameAndRegion.getT2()));
|
||||
|
||||
String beanLabel = beanLabel(isFunction, nameAndRegion.getT1(), beanType.getName(), "@Bean" + markerString);
|
||||
|
||||
InjectionPoint[] injectionPoints = ASTUtils.findInjectionPoints(method, doc);
|
||||
|
||||
Set<String> supertypes = new HashSet<>();
|
||||
ASTUtils.findSupertypes(beanType, supertypes);
|
||||
|
||||
Collection<Annotation> annotationsOnMethod = ASTUtils.getAnnotations(method);
|
||||
AnnotationMetadata[] annotations = ASTUtils.getAnnotationsMetadata(annotationsOnMethod, doc);
|
||||
|
||||
Bean beanDefinition = new Bean(nameAndRegion.getT1(), beanType.getQualifiedName(), location, injectionPoints, supertypes, annotations, false, beanLabel);
|
||||
|
||||
if (isWebfluxRouter) {
|
||||
WebfluxRouterSymbolProvider.createWebfluxElements(beanDefinition, method, context, doc);
|
||||
}
|
||||
|
||||
configBean.addChild(beanDefinition);
|
||||
|
||||
} catch (BadLocationException e) {
|
||||
log.error("", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static String beanLabel(boolean isFunctionBean, String beanName, String beanType, String markerString) {
|
||||
StringBuilder symbolLabel = new StringBuilder();
|
||||
symbolLabel.append('@');
|
||||
symbolLabel.append(isFunctionBean ? '>' : '+');
|
||||
symbolLabel.append(' ');
|
||||
symbolLabel.append('\'');
|
||||
symbolLabel.append(beanName);
|
||||
symbolLabel.append('\'');
|
||||
|
||||
markerString = markerString != null && markerString.length() > 0 ? " (" + markerString + ") " : " ";
|
||||
symbolLabel.append(markerString);
|
||||
|
||||
symbolLabel.append(beanType);
|
||||
return symbolLabel.toString();
|
||||
}
|
||||
|
||||
public static ITypeBinding getBeanType(MethodDeclaration method) {
|
||||
return method.getReturnType2().resolveBinding();
|
||||
}
|
||||
|
||||
public static boolean isFunctionBean(MethodDeclaration method) {
|
||||
String returnType = null;
|
||||
|
||||
if (method.getReturnType2().isParameterizedType()) {
|
||||
ParameterizedType paramType = (ParameterizedType) method.getReturnType2();
|
||||
Type type = paramType.getType();
|
||||
ITypeBinding typeBinding = type.resolveBinding();
|
||||
returnType = typeBinding.getBinaryName();
|
||||
}
|
||||
else {
|
||||
returnType = method.getReturnType2().resolveBinding().getQualifiedName();
|
||||
}
|
||||
|
||||
return FunctionUtils.FUNCTION_FUNCTION_TYPE.equals(returnType) || FunctionUtils.FUNCTION_CONSUMER_TYPE.equals(returnType)
|
||||
|| FunctionUtils.FUNCTION_SUPPLIER_TYPE.equals(returnType);
|
||||
}
|
||||
|
||||
public static String getAnnotations(MethodDeclaration method) {
|
||||
StringBuilder result = new StringBuilder();
|
||||
|
||||
List<?> modifiers = method.modifiers();
|
||||
for (Object modifier : modifiers) {
|
||||
if (modifier instanceof Annotation) {
|
||||
Annotation annotation = (Annotation) modifier;
|
||||
IAnnotationBinding annotationBinding = annotation.resolveAnnotationBinding();
|
||||
String type = annotationBinding.getAnnotationType().getBinaryName();
|
||||
|
||||
if (type != null && !Annotations.BEAN.equals(type)) {
|
||||
result.append(' ');
|
||||
result.append(annotation.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
public static boolean isMethodAbstract(MethodDeclaration method) {
|
||||
List<?> modifiers = method.modifiers();
|
||||
for (Object modifier : modifiers) {
|
||||
if (modifier instanceof Modifier && ((Modifier) modifier).isAbstract()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -10,21 +10,14 @@
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.beans;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jdt.core.dom.ASTNode;
|
||||
import org.eclipse.jdt.core.dom.Annotation;
|
||||
import org.eclipse.jdt.core.dom.Block;
|
||||
import org.eclipse.jdt.core.dom.IAnnotationBinding;
|
||||
import org.eclipse.jdt.core.dom.ITypeBinding;
|
||||
import org.eclipse.jdt.core.dom.MethodDeclaration;
|
||||
import org.eclipse.jdt.core.dom.Modifier;
|
||||
import org.eclipse.jdt.core.dom.ParameterizedType;
|
||||
import org.eclipse.jdt.core.dom.Type;
|
||||
import org.eclipse.jdt.core.dom.TypeDeclaration;
|
||||
import org.eclipse.lsp4j.Location;
|
||||
import org.eclipse.lsp4j.SymbolKind;
|
||||
@@ -32,18 +25,14 @@ import org.eclipse.lsp4j.WorkspaceSymbol;
|
||||
import org.eclipse.lsp4j.jsonrpc.messages.Either;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.ide.vscode.boot.java.Annotations;
|
||||
import org.springframework.ide.vscode.boot.java.handlers.AbstractSymbolProvider;
|
||||
import org.springframework.ide.vscode.boot.java.requestmapping.WebfluxRouterSymbolProvider;
|
||||
import org.springframework.ide.vscode.boot.java.handlers.SymbolProvider;
|
||||
import org.springframework.ide.vscode.boot.java.utils.ASTUtils;
|
||||
import org.springframework.ide.vscode.boot.java.utils.CachedSymbol;
|
||||
import org.springframework.ide.vscode.boot.java.utils.FunctionUtils;
|
||||
import org.springframework.ide.vscode.boot.java.utils.SpringIndexerJava.SCAN_PASS;
|
||||
import org.springframework.ide.vscode.boot.java.utils.SpringIndexerJavaContext;
|
||||
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.InjectionPoint;
|
||||
import org.springframework.ide.vscode.commons.protocol.spring.SpringIndexElement;
|
||||
import org.springframework.ide.vscode.commons.util.BadLocationException;
|
||||
import org.springframework.ide.vscode.commons.util.text.DocumentRegion;
|
||||
import org.springframework.ide.vscode.commons.util.text.TextDocument;
|
||||
@@ -54,7 +43,7 @@ import reactor.util.function.Tuple2;
|
||||
* @author Martin Lippert
|
||||
* @author Kris De Volder
|
||||
*/
|
||||
public class BeansSymbolProvider extends AbstractSymbolProvider {
|
||||
public class BeansSymbolProvider implements SymbolProvider {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(BeansSymbolProvider.class);
|
||||
|
||||
@@ -66,68 +55,24 @@ public class BeansSymbolProvider extends AbstractSymbolProvider {
|
||||
if (parent == null || !(parent instanceof MethodDeclaration)) return;
|
||||
|
||||
MethodDeclaration method = (MethodDeclaration) parent;
|
||||
if (isMethodAbstract(method)) return;
|
||||
if (BeansIndexer.isMethodAbstract(method)) return;
|
||||
|
||||
List<SpringIndexElement> childElements = new ArrayList<>();
|
||||
|
||||
boolean isWebfluxRouter = WebfluxRouterSymbolProvider.isWebfluxRouterBean(method);
|
||||
|
||||
// for webflux details, we need full method body ASTs
|
||||
if (isWebfluxRouter) {
|
||||
Block methodBody = method.getBody();
|
||||
if ((methodBody == null || methodBody.statements() == null || methodBody.statements().size() == 0)
|
||||
&& SCAN_PASS.ONE.equals(context.getPass())) {
|
||||
context.getNextPassFiles().add(context.getFile());
|
||||
return;
|
||||
}
|
||||
else {
|
||||
WebfluxRouterSymbolProvider.createWebfluxElements(method, context, doc, childElements);
|
||||
}
|
||||
} else if (!isWebfluxRouter && SCAN_PASS.TWO.equals(context.getPass())) {
|
||||
return;
|
||||
}
|
||||
|
||||
boolean isFunction = isFunctionBean(method);
|
||||
boolean isFunction = BeansIndexer.isFunctionBean(method);
|
||||
|
||||
ITypeBinding beanType = getBeanType(method);
|
||||
String markerString = getAnnotations(method);
|
||||
ITypeBinding beanType = BeansIndexer.getBeanType(method);
|
||||
String markerString = BeansIndexer.getAnnotations(method);
|
||||
|
||||
// lookup parent config
|
||||
SpringIndexElement configParent = findNearestConfigBean(context.getBeans(), doc.getUri());
|
||||
|
||||
for (Tuple2<String, DocumentRegion> nameAndRegion : BeanUtils.getBeanNamesFromBeanAnnotationWithRegions(node, doc)) {
|
||||
try {
|
||||
Location location = new Location(doc.getUri(), doc.toRange(nameAndRegion.getT2()));
|
||||
|
||||
WorkspaceSymbol symbol = new WorkspaceSymbol(
|
||||
beanLabel(isFunction, nameAndRegion.getT1(), beanType.getName(), "@Bean" + markerString),
|
||||
BeansIndexer.beanLabel(isFunction, nameAndRegion.getT1(), beanType.getName(), "@Bean" + markerString),
|
||||
SymbolKind.Interface,
|
||||
Either.forLeft(location)
|
||||
);
|
||||
|
||||
InjectionPoint[] injectionPoints = ASTUtils.findInjectionPoints(method, doc);
|
||||
|
||||
Set<String> supertypes = new HashSet<>();
|
||||
ASTUtils.findSupertypes(beanType, supertypes);
|
||||
|
||||
Collection<Annotation> annotationsOnMethod = ASTUtils.getAnnotations(method);
|
||||
AnnotationMetadata[] annotations = ASTUtils.getAnnotationsMetadata(annotationsOnMethod, doc);
|
||||
|
||||
Bean beanDefinition = new Bean(nameAndRegion.getT1(), beanType.getQualifiedName(), location, injectionPoints, supertypes, annotations, false, symbol.getName());
|
||||
if (childElements.size() > 0) {
|
||||
for (SpringIndexElement springIndexElement : childElements) {
|
||||
beanDefinition.addChild(springIndexElement);
|
||||
}
|
||||
}
|
||||
|
||||
context.getGeneratedSymbols().add(new CachedSymbol(context.getDocURI(), context.getLastModified(), symbol));
|
||||
|
||||
if (configParent != null) {
|
||||
configParent.addChild(beanDefinition);
|
||||
}
|
||||
else {
|
||||
context.getBeans().add(new CachedBean(context.getDocURI(), beanDefinition));
|
||||
}
|
||||
|
||||
} catch (BadLocationException e) {
|
||||
log.error("", e);
|
||||
@@ -135,22 +80,12 @@ public class BeansSymbolProvider extends AbstractSymbolProvider {
|
||||
}
|
||||
}
|
||||
|
||||
private SpringIndexElement findNearestConfigBean(List<CachedBean> beans, String docURI) {
|
||||
int i = beans.size() - 1;
|
||||
|
||||
while (i >= 0 && beans.get(i).getDocURI().equals(docURI)) {
|
||||
if (beans.get(i).getBean() instanceof Bean bean && bean.isConfiguration() && docURI.equals(docURI)) {
|
||||
return beans.get(i).getBean();
|
||||
}
|
||||
i--;
|
||||
}
|
||||
|
||||
return null;
|
||||
@Override
|
||||
public void addSymbols(TypeDeclaration typeDeclaration, SpringIndexerJavaContext context, TextDocument doc) {
|
||||
indexFunctionBeans(typeDeclaration, context, doc);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addSymbolsPass1(TypeDeclaration typeDeclaration, SpringIndexerJavaContext context, TextDocument doc) {
|
||||
// this checks function beans that are defined as implementations of Function interfaces
|
||||
private void indexFunctionBeans(TypeDeclaration typeDeclaration, SpringIndexerJavaContext context, TextDocument doc) {
|
||||
ITypeBinding functionBean = FunctionUtils.getFunctionBean(typeDeclaration, doc);
|
||||
if (functionBean != null) {
|
||||
try {
|
||||
@@ -159,7 +94,7 @@ public class BeansSymbolProvider extends AbstractSymbolProvider {
|
||||
Location beanLocation = new Location(doc.getUri(), doc.toRange(ASTUtils.nodeRegion(doc, typeDeclaration.getName())));
|
||||
|
||||
WorkspaceSymbol symbol = new WorkspaceSymbol(
|
||||
beanLabel(true, beanName, beanType.getName(), null),
|
||||
BeansIndexer.beanLabel(true, beanName, beanType.getName(), null),
|
||||
SymbolKind.Interface,
|
||||
Either.forLeft(beanLocation));
|
||||
|
||||
@@ -183,70 +118,4 @@ public class BeansSymbolProvider extends AbstractSymbolProvider {
|
||||
}
|
||||
}
|
||||
|
||||
public static String beanLabel(boolean isFunctionBean, String beanName, String beanType, String markerString) {
|
||||
StringBuilder symbolLabel = new StringBuilder();
|
||||
symbolLabel.append('@');
|
||||
symbolLabel.append(isFunctionBean ? '>' : '+');
|
||||
symbolLabel.append(' ');
|
||||
symbolLabel.append('\'');
|
||||
symbolLabel.append(beanName);
|
||||
symbolLabel.append('\'');
|
||||
|
||||
markerString = markerString != null && markerString.length() > 0 ? " (" + markerString + ") " : " ";
|
||||
symbolLabel.append(markerString);
|
||||
|
||||
symbolLabel.append(beanType);
|
||||
return symbolLabel.toString();
|
||||
}
|
||||
|
||||
protected ITypeBinding getBeanType(MethodDeclaration method) {
|
||||
return method.getReturnType2().resolveBinding();
|
||||
}
|
||||
|
||||
private boolean isFunctionBean(MethodDeclaration method) {
|
||||
String returnType = null;
|
||||
|
||||
if (method.getReturnType2().isParameterizedType()) {
|
||||
ParameterizedType paramType = (ParameterizedType) method.getReturnType2();
|
||||
Type type = paramType.getType();
|
||||
ITypeBinding typeBinding = type.resolveBinding();
|
||||
returnType = typeBinding.getBinaryName();
|
||||
}
|
||||
else {
|
||||
returnType = method.getReturnType2().resolveBinding().getQualifiedName();
|
||||
}
|
||||
|
||||
return FunctionUtils.FUNCTION_FUNCTION_TYPE.equals(returnType) || FunctionUtils.FUNCTION_CONSUMER_TYPE.equals(returnType)
|
||||
|| FunctionUtils.FUNCTION_SUPPLIER_TYPE.equals(returnType);
|
||||
}
|
||||
|
||||
private String getAnnotations(MethodDeclaration method) {
|
||||
StringBuilder result = new StringBuilder();
|
||||
|
||||
List<?> modifiers = method.modifiers();
|
||||
for (Object modifier : modifiers) {
|
||||
if (modifier instanceof Annotation) {
|
||||
Annotation annotation = (Annotation) modifier;
|
||||
IAnnotationBinding annotationBinding = annotation.resolveAnnotationBinding();
|
||||
String type = annotationBinding.getAnnotationType().getBinaryName();
|
||||
|
||||
if (type != null && !Annotations.BEAN.equals(type)) {
|
||||
result.append(' ');
|
||||
result.append(annotation.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
private boolean isMethodAbstract(MethodDeclaration method) {
|
||||
List<?> modifiers = method.modifiers();
|
||||
for (Object modifier : modifiers) {
|
||||
if (modifier instanceof Modifier && ((Modifier) modifier).isAbstract()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -14,7 +14,6 @@ import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
@@ -34,9 +33,13 @@ import org.eclipse.lsp4j.jsonrpc.messages.Either;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.ide.vscode.boot.java.Annotations;
|
||||
import org.springframework.ide.vscode.boot.java.annotations.AnnotationHierarchies;
|
||||
import org.springframework.ide.vscode.boot.java.events.EventListenerIndexElement;
|
||||
import org.springframework.ide.vscode.boot.java.events.EventListenerIndexer;
|
||||
import org.springframework.ide.vscode.boot.java.events.EventPublisherIndexElement;
|
||||
import org.springframework.ide.vscode.boot.java.handlers.AbstractSymbolProvider;
|
||||
import org.springframework.ide.vscode.boot.java.handlers.SymbolProvider;
|
||||
import org.springframework.ide.vscode.boot.java.reconcilers.RequiredCompleteAstException;
|
||||
import org.springframework.ide.vscode.boot.java.requestmapping.RequestMappingIndexer;
|
||||
import org.springframework.ide.vscode.boot.java.utils.ASTUtils;
|
||||
import org.springframework.ide.vscode.boot.java.utils.CachedSymbol;
|
||||
import org.springframework.ide.vscode.boot.java.utils.DefaultSymbolProvider;
|
||||
@@ -53,12 +56,12 @@ import org.springframework.ide.vscode.commons.util.text.TextDocument;
|
||||
* @author Martin Lippert
|
||||
* @author Kris De Volder
|
||||
*/
|
||||
public class ComponentSymbolProvider extends AbstractSymbolProvider {
|
||||
public class ComponentSymbolProvider implements SymbolProvider {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(ComponentSymbolProvider.class);
|
||||
|
||||
@Override
|
||||
protected void addSymbolsPass1(Annotation node, ITypeBinding annotationType, Collection<ITypeBinding> metaAnnotations, SpringIndexerJavaContext context, TextDocument doc) {
|
||||
public void addSymbols(Annotation node, ITypeBinding annotationType, Collection<ITypeBinding> metaAnnotations, SpringIndexerJavaContext context, TextDocument doc) {
|
||||
try {
|
||||
if (node != null && node.getParent() != null && node.getParent() instanceof TypeDeclaration) {
|
||||
createSymbol(node, annotationType, metaAnnotations, context, doc);
|
||||
@@ -69,7 +72,7 @@ public class ComponentSymbolProvider extends AbstractSymbolProvider {
|
||||
context.getBeans().add(new CachedBean(context.getDocURI(), new SimpleSymbolElement(symbol)));
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
catch (BadLocationException e) {
|
||||
log.error("", e);
|
||||
}
|
||||
}
|
||||
@@ -111,30 +114,106 @@ public class ComponentSymbolProvider extends AbstractSymbolProvider {
|
||||
|
||||
Bean beanDefinition = new Bean(beanName, beanType.getQualifiedName(), location, injectionPoints, supertypes, annotations, isConfiguration, symbol.getName());
|
||||
|
||||
// type implements event listener - move those already created event index elements under the bean node
|
||||
List<CachedBean> alreadyCreatedEventListenerChilds = context.getBeans().stream()
|
||||
.filter(cachedBean -> cachedBean.getDocURI().equals(doc.getUri()))
|
||||
.filter(cachedBean -> cachedBean.getBean() instanceof EventListenerIndexElement)
|
||||
.toList();
|
||||
|
||||
for (CachedBean eventListener : alreadyCreatedEventListenerChilds) {
|
||||
context.getBeans().remove(eventListener);
|
||||
beanDefinition.addChild(eventListener.getBean());
|
||||
}
|
||||
|
||||
// event publisher checks
|
||||
boolean usesEventPublisher = false;
|
||||
for (InjectionPoint injectionPoint : injectionPoints) {
|
||||
if (Annotations.EVENT_PUBLISHER.equals(injectionPoint.getType())) {
|
||||
context.getNextPassFiles().add(context.getFile());
|
||||
usesEventPublisher = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (usesEventPublisher) {
|
||||
if (context.isFullAst()) {
|
||||
scanEventPublisherInvocations(beanDefinition, node, annotationType, metaAnnotations, context, doc);
|
||||
}
|
||||
else {
|
||||
throw new RequiredCompleteAstException();
|
||||
}
|
||||
}
|
||||
|
||||
indexBeanMethods(beanDefinition, type, annotationType, metaAnnotations, context, doc);
|
||||
indexEventListeners(beanDefinition, type, annotationType, metaAnnotations, context, doc);
|
||||
indexEventListenerInterfaceImplementation(beanDefinition, type, context, doc);
|
||||
indexRequestMappings(beanDefinition, type, annotationType, metaAnnotations, context, doc);
|
||||
|
||||
context.getGeneratedSymbols().add(new CachedSymbol(context.getDocURI(), context.getLastModified(), symbol));
|
||||
context.getBeans().add(new CachedBean(context.getDocURI(), beanDefinition));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addSymbolsPass2(Annotation node, ITypeBinding annotationType, Collection<ITypeBinding> metaAnnotations, SpringIndexerJavaContext context, TextDocument doc) {
|
||||
private void indexBeanMethods(Bean bean, TypeDeclaration type, ITypeBinding annotationType, Collection<ITypeBinding> metaAnnotations, SpringIndexerJavaContext context, TextDocument doc) {
|
||||
AnnotationHierarchies annotationHierarchies = AnnotationHierarchies.get(type);
|
||||
if (bean.isConfiguration()) {
|
||||
MethodDeclaration[] methods = type.getMethods();
|
||||
if (methods == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < methods.length; i++) {
|
||||
MethodDeclaration methodDecl = methods[i];
|
||||
Collection<Annotation> annotations = ASTUtils.getAnnotations(methodDecl);
|
||||
|
||||
for (Annotation annotation : annotations) {
|
||||
ITypeBinding typeBinding = annotation.resolveTypeBinding();
|
||||
|
||||
boolean isBeanMethod = annotationHierarchies.isAnnotatedWith(typeBinding, Annotations.BEAN);
|
||||
if (isBeanMethod) {
|
||||
BeansIndexer.indexBeanMethod(bean, annotation, context, doc);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void indexEventListeners(Bean bean, TypeDeclaration type, ITypeBinding annotationType, Collection<ITypeBinding> metaAnnotations, SpringIndexerJavaContext context, TextDocument doc) {
|
||||
AnnotationHierarchies annotationHierarchies = AnnotationHierarchies.get(type);
|
||||
|
||||
MethodDeclaration[] methods = type.getMethods();
|
||||
if (methods == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < methods.length; i++) {
|
||||
MethodDeclaration methodDecl = methods[i];
|
||||
Collection<Annotation> annotations = ASTUtils.getAnnotations(methodDecl);
|
||||
|
||||
for (Annotation annotation : annotations) {
|
||||
ITypeBinding typeBinding = annotation.resolveTypeBinding();
|
||||
|
||||
boolean isEventListenerAnnotation = annotationHierarchies.isAnnotatedWith(typeBinding, Annotations.EVENT_LISTENER);
|
||||
if (isEventListenerAnnotation) {
|
||||
EventListenerIndexer.indexEventListener(bean, annotation, context, doc);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void indexRequestMappings(Bean controller, TypeDeclaration type, ITypeBinding annotationType, Collection<ITypeBinding> metaAnnotations, SpringIndexerJavaContext context, TextDocument doc) {
|
||||
AnnotationHierarchies annotationHierarchies = AnnotationHierarchies.get(type);
|
||||
boolean isController = annotationHierarchies.isAnnotatedWith(annotationType, Annotations.CONTROLLER);
|
||||
|
||||
if (isController) {
|
||||
MethodDeclaration[] methods = type.getMethods();
|
||||
if (methods == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < methods.length; i++) {
|
||||
MethodDeclaration methodDecl = methods[i];
|
||||
Collection<Annotation> annotations = ASTUtils.getAnnotations(methodDecl);
|
||||
|
||||
for (Annotation annotation : annotations) {
|
||||
ITypeBinding typeBinding = annotation.resolveTypeBinding();
|
||||
|
||||
boolean isRequestMappingAnnotation = annotationHierarchies.isAnnotatedWith(typeBinding, Annotations.SPRING_REQUEST_MAPPING);
|
||||
if (isRequestMappingAnnotation) {
|
||||
RequestMappingIndexer.indexRequestMapping(controller, annotation, context, doc);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void scanEventPublisherInvocations(Bean component, Annotation node, ITypeBinding annotationType, Collection<ITypeBinding> metaAnnotations, SpringIndexerJavaContext context, TextDocument doc) {
|
||||
TypeDeclaration type = (TypeDeclaration) node.getParent();
|
||||
type.accept(new ASTVisitor() {
|
||||
|
||||
@@ -161,11 +240,8 @@ public class ComponentSymbolProvider extends AbstractSymbolProvider {
|
||||
ASTUtils.findSupertypes(eventTypeBinding, typesFromhierarchy);
|
||||
|
||||
EventPublisherIndexElement eventPublisherIndexElement = new EventPublisherIndexElement(eventTypeBinding.getQualifiedName(), location, typesFromhierarchy);
|
||||
Bean publisherBeanElement = findBean(node, methodInvocation, context, doc);
|
||||
if (publisherBeanElement != null) {
|
||||
publisherBeanElement.addChild(eventPublisherIndexElement);
|
||||
}
|
||||
|
||||
component.addChild(eventPublisherIndexElement);
|
||||
|
||||
// symbol
|
||||
String symbolLabel = "@EventPublisher (" + eventTypeBinding.getName() + ")";
|
||||
WorkspaceSymbol symbol = new WorkspaceSymbol(symbolLabel, SymbolKind.Interface, Either.forLeft(location));
|
||||
@@ -184,7 +260,18 @@ public class ComponentSymbolProvider extends AbstractSymbolProvider {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addSymbolsPass1(TypeDeclaration typeDeclaration, SpringIndexerJavaContext context, TextDocument doc) {
|
||||
public void addSymbols(TypeDeclaration typeDeclaration, SpringIndexerJavaContext context, TextDocument doc) {
|
||||
AnnotationHierarchies annotationHierarchies = AnnotationHierarchies.get(typeDeclaration);
|
||||
boolean isComponment = annotationHierarchies.isAnnotatedWith(typeDeclaration.resolveBinding(), Annotations.COMPONENT);
|
||||
|
||||
// check for event listener implementations on classes that are not annotated with component, but created via bean methods (for example)
|
||||
if (!isComponment) {
|
||||
indexEventListenerInterfaceImplementation(null, typeDeclaration, context, doc);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void indexEventListenerInterfaceImplementation(Bean bean, TypeDeclaration typeDeclaration, SpringIndexerJavaContext context, TextDocument doc) {
|
||||
try {
|
||||
ITypeBinding typeBinding = typeDeclaration.resolveBinding();
|
||||
if (typeBinding == null) return;
|
||||
@@ -209,7 +296,13 @@ public class ComponentSymbolProvider extends AbstractSymbolProvider {
|
||||
AnnotationMetadata[] handleEventMethodAnnotations = ASTUtils.getAnnotationsMetadata(annotationsOnHandleEventMethod, doc);
|
||||
|
||||
EventListenerIndexElement eventElement = new EventListenerIndexElement(eventTypeFq, handleMethodLocation, typeBinding.getQualifiedName(), handleEventMethodAnnotations);
|
||||
context.getBeans().add(new CachedBean(doc.getUri(), eventElement));
|
||||
|
||||
if (bean != null) {
|
||||
bean.addChild(eventElement);
|
||||
}
|
||||
else {
|
||||
context.getBeans().add(new CachedBean(context.getDocURI(), eventElement));
|
||||
}
|
||||
}
|
||||
} catch (BadLocationException e) {
|
||||
log.error("", e);
|
||||
@@ -230,24 +323,6 @@ public class ComponentSymbolProvider extends AbstractSymbolProvider {
|
||||
return null;
|
||||
}
|
||||
|
||||
private Bean findBean(Annotation annotation, MethodInvocation methodInvocation, SpringIndexerJavaContext context, TextDocument doc) {
|
||||
TypeDeclaration declaringType = ASTUtils.findDeclaringType(methodInvocation);
|
||||
if (declaringType != null) {
|
||||
String beanName = BeanUtils.getBeanNameFromComponentAnnotation(annotation, declaringType);
|
||||
if (beanName != null) {
|
||||
Optional<Bean> first = context.getBeans().stream().filter(cachedBean -> cachedBean.getDocURI().equals(doc.getUri()))
|
||||
.map(cachedBean -> cachedBean.getBean())
|
||||
.filter(bean -> bean instanceof Bean)
|
||||
.map(bean -> (Bean) bean)
|
||||
.filter(bean -> bean.getName().equals(beanName))
|
||||
.findFirst();
|
||||
return first.get();
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected String beanLabel(String searchPrefix, String annotationTypeName, Collection<String> metaAnnotationNames, String beanName, String beanType) {
|
||||
StringBuilder symbolLabel = new StringBuilder();
|
||||
symbolLabel.append("@");
|
||||
|
||||
@@ -31,7 +31,7 @@ import org.eclipse.lsp4j.jsonrpc.messages.Tuple;
|
||||
import org.eclipse.lsp4j.jsonrpc.messages.Tuple.Two;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.ide.vscode.boot.java.handlers.AbstractSymbolProvider;
|
||||
import org.springframework.ide.vscode.boot.java.handlers.SymbolProvider;
|
||||
import org.springframework.ide.vscode.boot.java.utils.ASTUtils;
|
||||
import org.springframework.ide.vscode.boot.java.utils.CachedSymbol;
|
||||
import org.springframework.ide.vscode.boot.java.utils.SpringIndexerJavaContext;
|
||||
@@ -41,13 +41,12 @@ import org.springframework.ide.vscode.commons.protocol.spring.InjectionPoint;
|
||||
import org.springframework.ide.vscode.commons.util.BadLocationException;
|
||||
import org.springframework.ide.vscode.commons.util.text.TextDocument;
|
||||
|
||||
public class FeignClientSymbolProvider extends AbstractSymbolProvider {
|
||||
public class FeignClientSymbolProvider implements SymbolProvider {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(FeignClientSymbolProvider.class);
|
||||
|
||||
@Override
|
||||
protected void addSymbolsPass1(Annotation node, ITypeBinding annotationType, Collection<ITypeBinding> metaAnnotations,
|
||||
SpringIndexerJavaContext context, TextDocument doc) {
|
||||
public void addSymbols(Annotation node, ITypeBinding annotationType, Collection<ITypeBinding> metaAnnotations, SpringIndexerJavaContext context, TextDocument doc) {
|
||||
try {
|
||||
if (node != null && node.getParent() != null && node.getParent() instanceof TypeDeclaration) {
|
||||
Two<WorkspaceSymbol, Bean> result = createSymbol(node, annotationType, metaAnnotations, doc);
|
||||
@@ -58,7 +57,7 @@ public class FeignClientSymbolProvider extends AbstractSymbolProvider {
|
||||
context.getBeans().add(new CachedBean(context.getDocURI(), beanDefinition));
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
catch (BadLocationException e) {
|
||||
log.error("", e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.ide.vscode.boot.java.beans.BeanUtils;
|
||||
import org.springframework.ide.vscode.boot.java.beans.CachedBean;
|
||||
import org.springframework.ide.vscode.boot.java.handlers.AbstractSymbolProvider;
|
||||
import org.springframework.ide.vscode.boot.java.handlers.SymbolProvider;
|
||||
import org.springframework.ide.vscode.boot.java.utils.ASTUtils;
|
||||
import org.springframework.ide.vscode.boot.java.utils.CachedSymbol;
|
||||
import org.springframework.ide.vscode.boot.java.utils.SpringIndexerJavaContext;
|
||||
@@ -42,12 +42,12 @@ import reactor.util.function.Tuples;
|
||||
/**
|
||||
* @author Martin Lippert
|
||||
*/
|
||||
public class DataRepositorySymbolProvider extends AbstractSymbolProvider {
|
||||
public class DataRepositorySymbolProvider implements SymbolProvider {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(DataRepositorySymbolProvider.class);
|
||||
|
||||
@Override
|
||||
protected void addSymbolsPass1(TypeDeclaration typeDeclaration, SpringIndexerJavaContext context, TextDocument doc) {
|
||||
public void addSymbols(TypeDeclaration typeDeclaration, SpringIndexerJavaContext context, TextDocument doc) {
|
||||
// this checks spring data repository beans that are defined as extensions of the repository interface
|
||||
Tuple4<String, ITypeBinding, String, DocumentRegion> repositoryBean = getRepositoryBean(typeDeclaration, doc);
|
||||
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2025 Broadcom
|
||||
* 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:
|
||||
* Broadcom - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.events;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.jdt.core.dom.ASTNode;
|
||||
import org.eclipse.jdt.core.dom.Annotation;
|
||||
import org.eclipse.jdt.core.dom.ITypeBinding;
|
||||
import org.eclipse.jdt.core.dom.IVariableBinding;
|
||||
import org.eclipse.jdt.core.dom.MethodDeclaration;
|
||||
import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
|
||||
import org.eclipse.jdt.core.dom.TypeDeclaration;
|
||||
import org.eclipse.lsp4j.Location;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.ide.vscode.boot.java.utils.ASTUtils;
|
||||
import org.springframework.ide.vscode.boot.java.utils.SpringIndexerJavaContext;
|
||||
import org.springframework.ide.vscode.commons.protocol.spring.AnnotationMetadata;
|
||||
import org.springframework.ide.vscode.commons.protocol.spring.Bean;
|
||||
import org.springframework.ide.vscode.commons.util.BadLocationException;
|
||||
import org.springframework.ide.vscode.commons.util.text.DocumentRegion;
|
||||
import org.springframework.ide.vscode.commons.util.text.TextDocument;
|
||||
|
||||
/**
|
||||
* @author Martin Lippert
|
||||
*/
|
||||
public class EventListenerIndexer {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(EventListenerIndexer.class);
|
||||
|
||||
public static void indexEventListener(Bean component, Annotation node, SpringIndexerJavaContext context, TextDocument doc) {
|
||||
if (node == null) return;
|
||||
|
||||
ASTNode parent = node.getParent();
|
||||
if (parent == null || !(parent instanceof MethodDeclaration)) return;
|
||||
|
||||
MethodDeclaration method = (MethodDeclaration) parent;
|
||||
|
||||
try {
|
||||
// String symbolLabel = createEventListenerSymbolLabel(node, method);
|
||||
|
||||
// index element for event listener
|
||||
Collection<Annotation> annotationsOnMethod = ASTUtils.getAnnotations(method);
|
||||
AnnotationMetadata[] annotations = ASTUtils.getAnnotationsMetadata(annotationsOnMethod, doc);
|
||||
|
||||
ITypeBinding eventType = getEventType(node, method);
|
||||
DocumentRegion nodeRegion = ASTUtils.nodeRegion(doc, method.getName());
|
||||
Location location = new Location(doc.getUri(), nodeRegion.asRange());
|
||||
|
||||
String containerBeanType = null;
|
||||
TypeDeclaration type = ASTUtils.findDeclaringType(method);
|
||||
if (type != null) {
|
||||
ITypeBinding binding = type.resolveBinding();
|
||||
if (binding != null) {
|
||||
containerBeanType = binding.getQualifiedName();
|
||||
}
|
||||
}
|
||||
|
||||
EventListenerIndexElement eventListenerIndexElement = new EventListenerIndexElement(eventType != null ? eventType.getQualifiedName() : "", location, containerBeanType, annotations);
|
||||
component.addChild(eventListenerIndexElement);
|
||||
|
||||
} catch (BadLocationException e) {
|
||||
log.error("", e);
|
||||
}
|
||||
}
|
||||
|
||||
public static String createEventListenerSymbolLabel(Annotation node, MethodDeclaration method) {
|
||||
// event listener annotation type
|
||||
String annotationTypeName = getAnnotationTypeName(node);
|
||||
ITypeBinding eventType = getEventType(node, method);
|
||||
|
||||
if (annotationTypeName != null) {
|
||||
return "@" + annotationTypeName + (eventType != null ? " (" + eventType.getName() + ")" : "");
|
||||
}
|
||||
else {
|
||||
return node.toString();
|
||||
}
|
||||
}
|
||||
|
||||
public static ITypeBinding getEventType(Annotation node, MethodDeclaration method) {
|
||||
List<?> parameters = method.parameters();
|
||||
if (parameters != null && parameters.size() == 1) {
|
||||
SingleVariableDeclaration param = (SingleVariableDeclaration) parameters.get(0);
|
||||
|
||||
IVariableBinding paramBinding = param.resolveBinding();
|
||||
if (paramBinding != null) {
|
||||
ITypeBinding paramType = paramBinding.getType();
|
||||
return paramType != null ? paramType : null;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String getAnnotationTypeName(Annotation node) {
|
||||
ITypeBinding typeBinding = node.resolveTypeBinding();
|
||||
if (typeBinding != null) {
|
||||
return typeBinding.getName();
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -9,41 +9,34 @@
|
||||
* Broadcom - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.events;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.jdt.core.dom.ASTNode;
|
||||
import org.eclipse.jdt.core.dom.Annotation;
|
||||
import org.eclipse.jdt.core.dom.ITypeBinding;
|
||||
import org.eclipse.jdt.core.dom.IVariableBinding;
|
||||
import org.eclipse.jdt.core.dom.MethodDeclaration;
|
||||
import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
|
||||
import org.eclipse.jdt.core.dom.TypeDeclaration;
|
||||
import org.eclipse.lsp4j.Location;
|
||||
import org.eclipse.lsp4j.SymbolKind;
|
||||
import org.eclipse.lsp4j.WorkspaceSymbol;
|
||||
import org.eclipse.lsp4j.jsonrpc.messages.Either;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.ide.vscode.boot.java.beans.CachedBean;
|
||||
import org.springframework.ide.vscode.boot.java.handlers.AbstractSymbolProvider;
|
||||
import org.springframework.ide.vscode.boot.java.utils.ASTUtils;
|
||||
import org.springframework.ide.vscode.boot.java.handlers.SymbolProvider;
|
||||
import org.springframework.ide.vscode.boot.java.utils.CachedSymbol;
|
||||
import org.springframework.ide.vscode.boot.java.utils.SpringIndexerJavaContext;
|
||||
import org.springframework.ide.vscode.commons.protocol.spring.AnnotationMetadata;
|
||||
import org.springframework.ide.vscode.commons.util.BadLocationException;
|
||||
import org.springframework.ide.vscode.commons.util.text.DocumentRegion;
|
||||
import org.springframework.ide.vscode.commons.util.text.TextDocument;
|
||||
|
||||
/**
|
||||
* @author Martin Lippert
|
||||
*/
|
||||
public class EventListenerSymbolProvider extends AbstractSymbolProvider {
|
||||
public class EventListenerSymbolProvider implements SymbolProvider {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(EventListenerSymbolProvider.class);
|
||||
|
||||
@Override
|
||||
protected void addSymbolsPass1(Annotation node, ITypeBinding typeBinding, Collection<ITypeBinding> metaAnnotations, SpringIndexerJavaContext context, TextDocument doc) {
|
||||
public void addSymbols(Annotation node, ITypeBinding typeBinding, Collection<ITypeBinding> metaAnnotations, SpringIndexerJavaContext context, TextDocument doc) {
|
||||
if (node == null) return;
|
||||
|
||||
ASTNode parent = node.getParent();
|
||||
@@ -51,89 +44,17 @@ public class EventListenerSymbolProvider extends AbstractSymbolProvider {
|
||||
|
||||
MethodDeclaration method = (MethodDeclaration) parent;
|
||||
|
||||
|
||||
// symbol
|
||||
try {
|
||||
String symbolLabel = createEventListenerSymbolLabel(node, method);
|
||||
String symbolLabel = EventListenerIndexer.createEventListenerSymbolLabel(node, method);
|
||||
WorkspaceSymbol symbol = new WorkspaceSymbol(symbolLabel, SymbolKind.Interface,
|
||||
Either.forLeft(new Location(doc.getUri(), doc.toRange(node.getStartPosition(), node.getLength()))));
|
||||
|
||||
context.getGeneratedSymbols().add(new CachedSymbol(context.getDocURI(), context.getLastModified(), symbol));
|
||||
|
||||
|
||||
// index element for event listener
|
||||
Collection<Annotation> annotationsOnMethod = ASTUtils.getAnnotations(method);
|
||||
AnnotationMetadata[] annotations = ASTUtils.getAnnotationsMetadata(annotationsOnMethod, doc);
|
||||
|
||||
List<CachedBean> beans = context.getBeans();
|
||||
if (beans.size() > 0 ) {
|
||||
|
||||
CachedBean cachedBean = beans.get(beans.size() - 1);
|
||||
if (cachedBean.getDocURI().equals(doc.getUri())) {
|
||||
|
||||
ITypeBinding eventType = getEventType(node, method);
|
||||
DocumentRegion nodeRegion = ASTUtils.nodeRegion(doc, method.getName());
|
||||
Location location = new Location(doc.getUri(), nodeRegion.asRange());
|
||||
|
||||
String containerBeanType = null;
|
||||
TypeDeclaration type = ASTUtils.findDeclaringType(method);
|
||||
if (type != null) {
|
||||
ITypeBinding binding = type.resolveBinding();
|
||||
if (binding != null) {
|
||||
containerBeanType = binding.getQualifiedName();
|
||||
}
|
||||
}
|
||||
|
||||
cachedBean.getBean().addChild(new EventListenerIndexElement(eventType != null ? eventType.getQualifiedName() : "", location, containerBeanType, annotations));
|
||||
}
|
||||
}
|
||||
} catch (BadLocationException e) {
|
||||
}
|
||||
catch (BadLocationException e) {
|
||||
log.error("", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addSymbolsPass1(TypeDeclaration typeDeclaration, SpringIndexerJavaContext context, TextDocument doc) {
|
||||
super.addSymbolsPass1(typeDeclaration, context, doc);
|
||||
}
|
||||
|
||||
private String createEventListenerSymbolLabel(Annotation node, MethodDeclaration method) {
|
||||
// event listener annotation type
|
||||
String annotationTypeName = getAnnotationTypeName(node);
|
||||
ITypeBinding eventType = getEventType(node, method);
|
||||
|
||||
if (annotationTypeName != null) {
|
||||
return "@" + annotationTypeName + (eventType != null ? " (" + eventType.getName() + ")" : "");
|
||||
}
|
||||
else {
|
||||
return node.toString();
|
||||
}
|
||||
}
|
||||
|
||||
private ITypeBinding getEventType(Annotation node, MethodDeclaration method) {
|
||||
List<?> parameters = method.parameters();
|
||||
if (parameters != null && parameters.size() == 1) {
|
||||
SingleVariableDeclaration param = (SingleVariableDeclaration) parameters.get(0);
|
||||
|
||||
IVariableBinding paramBinding = param.resolveBinding();
|
||||
if (paramBinding != null) {
|
||||
ITypeBinding paramType = paramBinding.getType();
|
||||
return paramType != null ? paramType : null;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private String getAnnotationTypeName(Annotation node) {
|
||||
ITypeBinding typeBinding = node.resolveTypeBinding();
|
||||
if (typeBinding != null) {
|
||||
return typeBinding.getName();
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,81 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2019 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
|
||||
* https://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.handlers;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.eclipse.jdt.core.dom.Annotation;
|
||||
import org.eclipse.jdt.core.dom.ITypeBinding;
|
||||
import org.eclipse.jdt.core.dom.MethodDeclaration;
|
||||
import org.eclipse.jdt.core.dom.TypeDeclaration;
|
||||
import org.springframework.ide.vscode.boot.java.utils.SpringIndexerJava.SCAN_PASS;
|
||||
import org.springframework.ide.vscode.commons.util.text.TextDocument;
|
||||
import org.springframework.ide.vscode.boot.java.utils.SpringIndexerJavaContext;
|
||||
|
||||
/**
|
||||
* @author Martin Lippert
|
||||
*/
|
||||
public class AbstractSymbolProvider implements SymbolProvider {
|
||||
|
||||
@Override
|
||||
public void addSymbols(Annotation node, ITypeBinding typeBinding, Collection<ITypeBinding> metaAnnotations, SpringIndexerJavaContext context, TextDocument doc) {
|
||||
if (SCAN_PASS.ONE.equals(context.getPass())) {
|
||||
addSymbolsPass1(node, typeBinding, metaAnnotations, context, doc);
|
||||
}
|
||||
else if (SCAN_PASS.TWO.equals(context.getPass())) {
|
||||
addSymbolsPass2(node, typeBinding, metaAnnotations, context, doc);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addSymbols(TypeDeclaration typeDeclaration, SpringIndexerJavaContext context, TextDocument doc) {
|
||||
if (SCAN_PASS.ONE.equals(context.getPass())) {
|
||||
addSymbolsPass1(typeDeclaration, context, doc);
|
||||
}
|
||||
else if (SCAN_PASS.TWO.equals(context.getPass())) {
|
||||
addSymbolsPass2(typeDeclaration, context, doc);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addSymbols(MethodDeclaration methodDeclaration, SpringIndexerJavaContext context, TextDocument doc) {
|
||||
if (SCAN_PASS.ONE.equals(context.getPass())) {
|
||||
addSymbolsPass1(methodDeclaration, context, doc);
|
||||
}
|
||||
else if (SCAN_PASS.TWO.equals(context.getPass())) {
|
||||
addSymbolsPass2(methodDeclaration, context, doc);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// implementations can decide whether to implement just pass1 or if they need 2 phases, they would have to implement both methods (pass1 + pass2)
|
||||
//
|
||||
|
||||
protected void addSymbolsPass1(Annotation node, ITypeBinding typeBinding, Collection<ITypeBinding> metaAnnotations, SpringIndexerJavaContext context, TextDocument doc) {
|
||||
}
|
||||
|
||||
protected void addSymbolsPass1(TypeDeclaration typeDeclaration, SpringIndexerJavaContext context, TextDocument doc) {
|
||||
}
|
||||
|
||||
protected void addSymbolsPass1(MethodDeclaration methodDeclaration, SpringIndexerJavaContext context, TextDocument doc) {
|
||||
}
|
||||
|
||||
protected void addSymbolsPass2(Annotation node, ITypeBinding typeBinding, Collection<ITypeBinding> metaAnnotations, SpringIndexerJavaContext context, TextDocument doc) {
|
||||
}
|
||||
|
||||
protected void addSymbolsPass2(TypeDeclaration typeDeclaration, SpringIndexerJavaContext context, TextDocument doc) {
|
||||
}
|
||||
|
||||
protected void addSymbolsPass2(MethodDeclaration methodDeclaration, SpringIndexerJavaContext context, TextDocument doc) {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017, 2019 Pivotal, Inc.
|
||||
* Copyright (c) 2017, 2025 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
|
||||
@@ -25,8 +25,8 @@ import org.springframework.ide.vscode.commons.util.text.TextDocument;
|
||||
*/
|
||||
public interface SymbolProvider {
|
||||
|
||||
void addSymbols(Annotation node, ITypeBinding typeBinding, Collection<ITypeBinding> metaAnnotations, SpringIndexerJavaContext context, TextDocument doc);
|
||||
void addSymbols(TypeDeclaration typeDeclaration, SpringIndexerJavaContext context, TextDocument doc);
|
||||
void addSymbols(MethodDeclaration methodDeclaration, SpringIndexerJavaContext context, TextDocument doc);
|
||||
default void addSymbols(Annotation node, ITypeBinding typeBinding, Collection<ITypeBinding> metaAnnotations, SpringIndexerJavaContext context, TextDocument doc) {};
|
||||
default void addSymbols(TypeDeclaration typeDeclaration, SpringIndexerJavaContext context, TextDocument doc) {};
|
||||
default void addSymbols(MethodDeclaration methodDeclaration, SpringIndexerJavaContext context, TextDocument doc) {};
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,310 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017, 2025 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
|
||||
* https://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.requestmapping;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.eclipse.jdt.core.dom.ASTNode;
|
||||
import org.eclipse.jdt.core.dom.Annotation;
|
||||
import org.eclipse.jdt.core.dom.Expression;
|
||||
import org.eclipse.jdt.core.dom.IAnnotationBinding;
|
||||
import org.eclipse.jdt.core.dom.IMemberValuePairBinding;
|
||||
import org.eclipse.jdt.core.dom.ITypeBinding;
|
||||
import org.eclipse.jdt.core.dom.MemberValuePair;
|
||||
import org.eclipse.jdt.core.dom.MethodDeclaration;
|
||||
import org.eclipse.jdt.core.dom.NormalAnnotation;
|
||||
import org.eclipse.jdt.core.dom.SingleMemberAnnotation;
|
||||
import org.eclipse.jdt.core.dom.TypeDeclaration;
|
||||
import org.eclipse.lsp4j.Location;
|
||||
import org.eclipse.lsp4j.WorkspaceSymbol;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.ide.vscode.boot.java.Annotations;
|
||||
import org.springframework.ide.vscode.boot.java.utils.ASTUtils;
|
||||
import org.springframework.ide.vscode.boot.java.utils.SpringIndexerJavaContext;
|
||||
import org.springframework.ide.vscode.commons.protocol.spring.Bean;
|
||||
import org.springframework.ide.vscode.commons.util.text.TextDocument;
|
||||
|
||||
/**
|
||||
* @author Martin Lippert
|
||||
*/
|
||||
public class RequestMappingIndexer {
|
||||
|
||||
private static final Set<String> ATTRIBUTE_NAME_VALUE_PATH = Set.of("value", "path");
|
||||
private static final Set<String> ATTRIBUTE_NAME_METHOD = Set.of("method");
|
||||
private static final Set<String> ATTRIBUTE_NAME_CONSUMES = Set.of("consumes");
|
||||
private static final Set<String> ATTRIBUTE_NAME_PRODUCES = Set.of("produces");
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(RequestMappingIndexer.class);
|
||||
|
||||
public static void indexRequestMapping(Bean controller, Annotation node, SpringIndexerJavaContext context, TextDocument doc) {
|
||||
|
||||
if (node.getParent() instanceof MethodDeclaration) {
|
||||
try {
|
||||
Location location = new Location(doc.getUri(), doc.toRange(node.getStartPosition(), node.getLength()));
|
||||
String[] path = getPath(node, context);
|
||||
String[] parentPath = getParentPath(node, context);
|
||||
String[] methods = getMethod(node, context);
|
||||
String[] contentTypes = getContentTypes(node, context);
|
||||
String[] acceptTypes = getAcceptTypes(node, context);
|
||||
|
||||
Stream<String> stream = parentPath == null ? Stream.of("") : Arrays.stream(parentPath);
|
||||
stream.filter(Objects::nonNull)
|
||||
.flatMap(parent -> (path == null ? Stream.<String>empty() : Arrays.stream(path))
|
||||
.filter(Objects::nonNull).map(p -> {
|
||||
return combinePath(parent, p);
|
||||
}))
|
||||
.forEach(p -> {
|
||||
// symbol
|
||||
WorkspaceSymbol symbol = RouteUtils.createRouteSymbol(location, p, methods, contentTypes, acceptTypes);
|
||||
|
||||
// index element for request mapping
|
||||
RequestMappingIndexElement requestMappingIndexElement = new RequestMappingIndexElement(p, methods, contentTypes, acceptTypes, location.getRange(), symbol.getName());
|
||||
controller.addChild(requestMappingIndexElement);
|
||||
});
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("problem occured while scanning for request mapping symbols from " + doc.getUri(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static String combinePath(String parent, String path) {
|
||||
String separator = !parent.endsWith("/") && !path.startsWith("/") && !path.isEmpty() ? "/" : "";
|
||||
String resultPath = parent + separator + path;
|
||||
|
||||
String result = resultPath.startsWith("/") ? resultPath : "/" + resultPath;
|
||||
return result;
|
||||
}
|
||||
|
||||
public static String[] getPath(Annotation node, SpringIndexerJavaContext context) {
|
||||
String[] result = getAttributeValuesFromAnnotation(node, context, ATTRIBUTE_NAME_VALUE_PATH);
|
||||
|
||||
if (result == null && node.isSingleMemberAnnotation()) {
|
||||
SingleMemberAnnotation singleNode = (SingleMemberAnnotation) node;
|
||||
Expression expression = singleNode.getValue();
|
||||
result = ASTUtils.getExpressionValueAsArray(expression, context::addDependency);
|
||||
}
|
||||
|
||||
return result != null ? result : new String[] { "" };
|
||||
}
|
||||
|
||||
public static String[] getParentPath(Annotation node, SpringIndexerJavaContext context) {
|
||||
Annotation parentAnnotation = getAnnotationFromClassLevel(node);
|
||||
if (parentAnnotation != null) {
|
||||
return getPath(parentAnnotation, context);
|
||||
}
|
||||
else {
|
||||
return getAttributeValuesFromSupertypes(node, context, ATTRIBUTE_NAME_VALUE_PATH);
|
||||
}
|
||||
}
|
||||
|
||||
public static String[] getAcceptTypes(Annotation node, SpringIndexerJavaContext context) {
|
||||
return getAttributeValues(node, context, ATTRIBUTE_NAME_CONSUMES);
|
||||
}
|
||||
|
||||
public static String[] getContentTypes(Annotation node, SpringIndexerJavaContext context) {
|
||||
return getAttributeValues(node, context, ATTRIBUTE_NAME_PRODUCES);
|
||||
}
|
||||
|
||||
public static String[] getMethod(Annotation node, SpringIndexerJavaContext context) {
|
||||
// extract from annotation type
|
||||
String[] methods = getRequestMethod(node);
|
||||
|
||||
// extract from annotation params
|
||||
if (methods == null) {
|
||||
methods = getAttributeValues(node, context, ATTRIBUTE_NAME_METHOD);
|
||||
}
|
||||
return methods;
|
||||
}
|
||||
|
||||
private static String[] getRequestMethod(Annotation annotation) {
|
||||
ITypeBinding type = annotation.resolveTypeBinding();
|
||||
if (type != null) {
|
||||
switch (type.getQualifiedName()) {
|
||||
case Annotations.SPRING_GET_MAPPING:
|
||||
return new String[] { "GET" };
|
||||
case Annotations.SPRING_POST_MAPPING:
|
||||
return new String[] { "POST" };
|
||||
case Annotations.SPRING_DELETE_MAPPING:
|
||||
return new String[] { "DELETE" };
|
||||
case Annotations.SPRING_PUT_MAPPING:
|
||||
return new String[] { "PUT" };
|
||||
case Annotations.SPRING_PATCH_MAPPING:
|
||||
return new String[] { "PATCH" };
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static String[] getAttributeValues(Annotation node, SpringIndexerJavaContext context, Set<String> attributeNames) {
|
||||
String[] result = getAttributeValuesFromAnnotation(node, context, attributeNames);
|
||||
|
||||
// extract from parent annotations
|
||||
if (result == null) {
|
||||
Annotation parentAnnotation = getAnnotationFromClassLevel(node);
|
||||
if (parentAnnotation != null) {
|
||||
result = getAttributeValuesFromAnnotation(parentAnnotation, context, attributeNames);
|
||||
}
|
||||
else {
|
||||
result = getAttributeValuesFromSupertypes(node, context, attributeNames);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static String[] getAttributeValuesFromAnnotation(Annotation node, SpringIndexerJavaContext context, Set<String> attributeNames) {
|
||||
if (node.isNormalAnnotation()) {
|
||||
NormalAnnotation normNode = (NormalAnnotation) node;
|
||||
List<?> values = normNode.values();
|
||||
for (Iterator<?> iterator = values.iterator(); iterator.hasNext();) {
|
||||
Object object = iterator.next();
|
||||
if (object instanceof MemberValuePair) {
|
||||
MemberValuePair pair = (MemberValuePair) object;
|
||||
String valueName = pair.getName().getIdentifier();
|
||||
if (valueName != null && attributeNames.contains(valueName)) {
|
||||
Expression expression = pair.getValue();
|
||||
return ASTUtils.getExpressionValueAsArray(expression, context::addDependency);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static String[] getAttributeValuesFromSupertypes(Annotation node, SpringIndexerJavaContext context, Set<String> attributeNames) {
|
||||
IAnnotationBinding annotationBinding = getAnnotationFromSupertypes(node, context);
|
||||
IMemberValuePairBinding valuePair = getValuePair(annotationBinding, attributeNames);
|
||||
|
||||
ASTUtils.MemberValuePairAndType result = ASTUtils.getValuesFromValuePair(valuePair);
|
||||
|
||||
if (result != null) {
|
||||
if (result.dereferencedType != null) {
|
||||
context.addDependency(result.dereferencedType);
|
||||
}
|
||||
return result.values;
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static IMemberValuePairBinding getValuePair(IAnnotationBinding annotationBinding, Set<String> names) {
|
||||
if (annotationBinding != null) {
|
||||
IMemberValuePairBinding[] valuePairs = annotationBinding.getDeclaredMemberValuePairs();
|
||||
|
||||
if (valuePairs != null ) {
|
||||
|
||||
for (int j = 0; j < valuePairs.length; j++) {
|
||||
String valueName = valuePairs[j].getName();
|
||||
|
||||
if (valueName != null && names.contains(valueName)) {
|
||||
return valuePairs[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Annotation getAnnotationFromClassLevel(Annotation node) {
|
||||
// lookup class level request mapping annotation
|
||||
ASTNode parent = node.getParent() != null ? node.getParent().getParent() : null;
|
||||
while (parent != null && !(parent instanceof TypeDeclaration)) {
|
||||
parent = parent.getParent();
|
||||
}
|
||||
|
||||
if (parent != null) {
|
||||
TypeDeclaration type = (TypeDeclaration) parent;
|
||||
List<?> modifiers = type.modifiers();
|
||||
Iterator<?> iterator = modifiers.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
Object modifier = iterator.next();
|
||||
if (modifier instanceof Annotation) {
|
||||
Annotation annotation = (Annotation) modifier;
|
||||
ITypeBinding resolvedType = annotation.resolveTypeBinding();
|
||||
String annotationType = resolvedType.getQualifiedName();
|
||||
if (annotationType != null && Annotations.SPRING_REQUEST_MAPPING.equals(annotationType)) {
|
||||
return annotation;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static IAnnotationBinding getAnnotationFromSupertypes(Annotation node, SpringIndexerJavaContext context) {
|
||||
ASTNode parent = node.getParent() != null ? node.getParent().getParent() : null;
|
||||
while (parent != null && !(parent instanceof TypeDeclaration)) {
|
||||
parent = parent.getParent();
|
||||
}
|
||||
|
||||
if (parent != null) {
|
||||
TypeDeclaration type = (TypeDeclaration) parent;
|
||||
ITypeBinding typeBinding = type.resolveBinding();
|
||||
if (typeBinding != null) {
|
||||
return findFirstRequestMappingAnnotation(typeBinding);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static IAnnotationBinding findFirstRequestMappingAnnotation(ITypeBinding start) {
|
||||
if (start == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
IAnnotationBinding found = getRequestMappingAnnotation(start);
|
||||
if (found != null) {
|
||||
return found;
|
||||
}
|
||||
else {
|
||||
// search interfaces first
|
||||
ITypeBinding[] interfaces = start.getInterfaces();
|
||||
if (interfaces != null) {
|
||||
for (int i = 0; i < interfaces.length; i++) {
|
||||
found = findFirstRequestMappingAnnotation(interfaces[i]);
|
||||
if (found != null) {
|
||||
return found;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// search superclass second
|
||||
ITypeBinding superclass = start.getSuperclass();
|
||||
if (superclass != null) {
|
||||
return findFirstRequestMappingAnnotation(superclass);
|
||||
}
|
||||
|
||||
// nothing found
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static IAnnotationBinding getRequestMappingAnnotation(ITypeBinding typeBinding) {
|
||||
IAnnotationBinding[] annotations = typeBinding.getAnnotations();
|
||||
for (int i = 0; i < annotations.length; i++) {
|
||||
if (annotations[i].getAnnotationType() != null && Annotations.SPRING_REQUEST_MAPPING.equals(annotations[i].getAnnotationType().getQualifiedName())) {
|
||||
return annotations[i];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -12,329 +12,57 @@ package org.springframework.ide.vscode.boot.java.requestmapping;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.eclipse.jdt.core.dom.ASTNode;
|
||||
import org.eclipse.jdt.core.dom.Annotation;
|
||||
import org.eclipse.jdt.core.dom.Expression;
|
||||
import org.eclipse.jdt.core.dom.IAnnotationBinding;
|
||||
import org.eclipse.jdt.core.dom.IMemberValuePairBinding;
|
||||
import org.eclipse.jdt.core.dom.ITypeBinding;
|
||||
import org.eclipse.jdt.core.dom.MemberValuePair;
|
||||
import org.eclipse.jdt.core.dom.MethodDeclaration;
|
||||
import org.eclipse.jdt.core.dom.NormalAnnotation;
|
||||
import org.eclipse.jdt.core.dom.SingleMemberAnnotation;
|
||||
import org.eclipse.jdt.core.dom.TypeDeclaration;
|
||||
import org.eclipse.lsp4j.Location;
|
||||
import org.eclipse.lsp4j.WorkspaceSymbol;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.ide.vscode.boot.java.Annotations;
|
||||
import org.springframework.ide.vscode.boot.java.beans.CachedBean;
|
||||
import org.springframework.ide.vscode.boot.java.handlers.AbstractSymbolProvider;
|
||||
import org.springframework.ide.vscode.boot.java.utils.ASTUtils;
|
||||
import org.springframework.ide.vscode.boot.java.handlers.SymbolProvider;
|
||||
import org.springframework.ide.vscode.boot.java.utils.CachedSymbol;
|
||||
import org.springframework.ide.vscode.boot.java.utils.SpringIndexerJavaContext;
|
||||
import org.springframework.ide.vscode.commons.protocol.spring.Bean;
|
||||
import org.springframework.ide.vscode.commons.protocol.spring.SpringIndexElement;
|
||||
import org.springframework.ide.vscode.commons.util.BadLocationException;
|
||||
import org.springframework.ide.vscode.commons.util.text.TextDocument;
|
||||
|
||||
/**
|
||||
* @author Martin Lippert
|
||||
*/
|
||||
public class RequestMappingSymbolProvider extends AbstractSymbolProvider {
|
||||
|
||||
private static final Set<String> ATTRIBUTE_NAME_VALUE_PATH = Set.of("value", "path");
|
||||
private static final Set<String> ATTRIBUTE_NAME_METHOD = Set.of("method");
|
||||
private static final Set<String> ATTRIBUTE_NAME_CONSUMES = Set.of("consumes");
|
||||
private static final Set<String> ATTRIBUTE_NAME_PRODUCES = Set.of("produces");
|
||||
public class RequestMappingSymbolProvider implements SymbolProvider {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(RequestMappingSymbolProvider.class);
|
||||
|
||||
@Override
|
||||
protected void addSymbolsPass1(Annotation node, ITypeBinding annotationType, Collection<ITypeBinding> metaAnnotations, SpringIndexerJavaContext context, TextDocument doc) {
|
||||
public void addSymbols(Annotation node, ITypeBinding annotationType, Collection<ITypeBinding> metaAnnotations, SpringIndexerJavaContext context, TextDocument doc) {
|
||||
|
||||
if (node.getParent() instanceof MethodDeclaration) {
|
||||
try {
|
||||
Location location = new Location(doc.getUri(), doc.toRange(node.getStartPosition(), node.getLength()));
|
||||
String[] path = getPath(node, context);
|
||||
String[] parentPath = getParentPath(node, context);
|
||||
String[] methods = getMethod(node, context);
|
||||
String[] contentTypes = getContentTypes(node, context);
|
||||
String[] acceptTypes = getAcceptTypes(node, context);
|
||||
String[] path = RequestMappingIndexer.getPath(node, context);
|
||||
String[] parentPath = RequestMappingIndexer.getParentPath(node, context);
|
||||
String[] methods = RequestMappingIndexer.getMethod(node, context);
|
||||
String[] contentTypes = RequestMappingIndexer.getContentTypes(node, context);
|
||||
String[] acceptTypes = RequestMappingIndexer.getAcceptTypes(node, context);
|
||||
|
||||
Stream<String> stream = parentPath == null ? Stream.of("") : Arrays.stream(parentPath);
|
||||
stream.filter(Objects::nonNull)
|
||||
.flatMap(parent -> (path == null ? Stream.<String>empty() : Arrays.stream(path))
|
||||
.filter(Objects::nonNull).map(p -> {
|
||||
return combinePath(parent, p);
|
||||
return RequestMappingIndexer.combinePath(parent, p);
|
||||
}))
|
||||
.forEach(p -> {
|
||||
// symbol
|
||||
WorkspaceSymbol symbol = RouteUtils.createRouteSymbol(location, p, methods, contentTypes, acceptTypes);
|
||||
context.getGeneratedSymbols().add(new CachedSymbol(context.getDocURI(), context.getLastModified(), symbol));
|
||||
|
||||
// index element for request mapping
|
||||
SpringIndexElement parent = getPotentialParentIndexNode(context);
|
||||
if (parent != null) {
|
||||
parent.addChild(new RequestMappingIndexElement(p, methods, contentTypes, acceptTypes, location.getRange(), symbol.getName()));
|
||||
}
|
||||
else {
|
||||
context.getBeans().add(new CachedBean(doc.getUri(), new RequestMappingIndexElement(p, methods, contentTypes, acceptTypes, location.getRange(), symbol.getName())));
|
||||
}
|
||||
});
|
||||
|
||||
} catch (Exception e) {
|
||||
} catch (BadLocationException e) {
|
||||
log.error("problem occured while scanning for request mapping symbols from " + doc.getUri(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private SpringIndexElement getPotentialParentIndexNode(SpringIndexerJavaContext context) {
|
||||
List<CachedBean> beans = context.getBeans();
|
||||
|
||||
for (int i = beans.size() - 1; i >= 0; i--) {
|
||||
CachedBean cachedBean = beans.get(i);
|
||||
|
||||
if (!cachedBean.getDocURI().equals(context.getDocURI())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (cachedBean.getBean() instanceof Bean bean) {
|
||||
return bean;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private String combinePath(String parent, String path) {
|
||||
String separator = !parent.endsWith("/") && !path.startsWith("/") && !path.isEmpty() ? "/" : "";
|
||||
String resultPath = parent + separator + path;
|
||||
|
||||
String result = resultPath.startsWith("/") ? resultPath : "/" + resultPath;
|
||||
return result;
|
||||
}
|
||||
|
||||
private String[] getPath(Annotation node, SpringIndexerJavaContext context) {
|
||||
String[] result = getAttributeValuesFromAnnotation(node, context, ATTRIBUTE_NAME_VALUE_PATH);
|
||||
|
||||
if (result == null && node.isSingleMemberAnnotation()) {
|
||||
SingleMemberAnnotation singleNode = (SingleMemberAnnotation) node;
|
||||
Expression expression = singleNode.getValue();
|
||||
result = ASTUtils.getExpressionValueAsArray(expression, context::addDependency);
|
||||
}
|
||||
|
||||
return result != null ? result : new String[] { "" };
|
||||
}
|
||||
|
||||
private String[] getParentPath(Annotation node, SpringIndexerJavaContext context) {
|
||||
Annotation parentAnnotation = getAnnotationFromClassLevel(node);
|
||||
if (parentAnnotation != null) {
|
||||
return getPath(parentAnnotation, context);
|
||||
}
|
||||
else {
|
||||
return getAttributeValuesFromSupertypes(node, context, ATTRIBUTE_NAME_VALUE_PATH);
|
||||
}
|
||||
}
|
||||
|
||||
private String[] getAcceptTypes(Annotation node, SpringIndexerJavaContext context) {
|
||||
return getAttributeValues(node, context, ATTRIBUTE_NAME_CONSUMES);
|
||||
}
|
||||
|
||||
private String[] getContentTypes(Annotation node, SpringIndexerJavaContext context) {
|
||||
return getAttributeValues(node, context, ATTRIBUTE_NAME_PRODUCES);
|
||||
}
|
||||
|
||||
private String[] getMethod(Annotation node, SpringIndexerJavaContext context) {
|
||||
// extract from annotation type
|
||||
String[] methods = getRequestMethod(node);
|
||||
|
||||
// extract from annotation params
|
||||
if (methods == null) {
|
||||
methods = getAttributeValues(node, context, ATTRIBUTE_NAME_METHOD);
|
||||
}
|
||||
return methods;
|
||||
}
|
||||
|
||||
private String[] getRequestMethod(Annotation annotation) {
|
||||
ITypeBinding type = annotation.resolveTypeBinding();
|
||||
if (type != null) {
|
||||
switch (type.getQualifiedName()) {
|
||||
case Annotations.SPRING_GET_MAPPING:
|
||||
return new String[] { "GET" };
|
||||
case Annotations.SPRING_POST_MAPPING:
|
||||
return new String[] { "POST" };
|
||||
case Annotations.SPRING_DELETE_MAPPING:
|
||||
return new String[] { "DELETE" };
|
||||
case Annotations.SPRING_PUT_MAPPING:
|
||||
return new String[] { "PUT" };
|
||||
case Annotations.SPRING_PATCH_MAPPING:
|
||||
return new String[] { "PATCH" };
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private String[] getAttributeValues(Annotation node, SpringIndexerJavaContext context, Set<String> attributeNames) {
|
||||
String[] result = getAttributeValuesFromAnnotation(node, context, attributeNames);
|
||||
|
||||
// extract from parent annotations
|
||||
if (result == null) {
|
||||
Annotation parentAnnotation = getAnnotationFromClassLevel(node);
|
||||
if (parentAnnotation != null) {
|
||||
result = getAttributeValuesFromAnnotation(parentAnnotation, context, attributeNames);
|
||||
}
|
||||
else {
|
||||
result = getAttributeValuesFromSupertypes(node, context, attributeNames);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private String[] getAttributeValuesFromAnnotation(Annotation node, SpringIndexerJavaContext context, Set<String> attributeNames) {
|
||||
if (node.isNormalAnnotation()) {
|
||||
NormalAnnotation normNode = (NormalAnnotation) node;
|
||||
List<?> values = normNode.values();
|
||||
for (Iterator<?> iterator = values.iterator(); iterator.hasNext();) {
|
||||
Object object = iterator.next();
|
||||
if (object instanceof MemberValuePair) {
|
||||
MemberValuePair pair = (MemberValuePair) object;
|
||||
String valueName = pair.getName().getIdentifier();
|
||||
if (valueName != null && attributeNames.contains(valueName)) {
|
||||
Expression expression = pair.getValue();
|
||||
return ASTUtils.getExpressionValueAsArray(expression, context::addDependency);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private String[] getAttributeValuesFromSupertypes(Annotation node, SpringIndexerJavaContext context, Set<String> attributeNames) {
|
||||
IAnnotationBinding annotationBinding = getAnnotationFromSupertypes(node, context);
|
||||
IMemberValuePairBinding valuePair = getValuePair(annotationBinding, attributeNames);
|
||||
|
||||
ASTUtils.MemberValuePairAndType result = ASTUtils.getValuesFromValuePair(valuePair);
|
||||
|
||||
if (result != null) {
|
||||
if (result.dereferencedType != null) {
|
||||
context.addDependency(result.dereferencedType);
|
||||
}
|
||||
return result.values;
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private IMemberValuePairBinding getValuePair(IAnnotationBinding annotationBinding, Set<String> names) {
|
||||
if (annotationBinding != null) {
|
||||
IMemberValuePairBinding[] valuePairs = annotationBinding.getDeclaredMemberValuePairs();
|
||||
|
||||
if (valuePairs != null ) {
|
||||
|
||||
for (int j = 0; j < valuePairs.length; j++) {
|
||||
String valueName = valuePairs[j].getName();
|
||||
|
||||
if (valueName != null && names.contains(valueName)) {
|
||||
return valuePairs[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private Annotation getAnnotationFromClassLevel(Annotation node) {
|
||||
// lookup class level request mapping annotation
|
||||
ASTNode parent = node.getParent() != null ? node.getParent().getParent() : null;
|
||||
while (parent != null && !(parent instanceof TypeDeclaration)) {
|
||||
parent = parent.getParent();
|
||||
}
|
||||
|
||||
if (parent != null) {
|
||||
TypeDeclaration type = (TypeDeclaration) parent;
|
||||
List<?> modifiers = type.modifiers();
|
||||
Iterator<?> iterator = modifiers.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
Object modifier = iterator.next();
|
||||
if (modifier instanceof Annotation) {
|
||||
Annotation annotation = (Annotation) modifier;
|
||||
ITypeBinding resolvedType = annotation.resolveTypeBinding();
|
||||
String annotationType = resolvedType.getQualifiedName();
|
||||
if (annotationType != null && Annotations.SPRING_REQUEST_MAPPING.equals(annotationType)) {
|
||||
return annotation;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private IAnnotationBinding getAnnotationFromSupertypes(Annotation node, SpringIndexerJavaContext context) {
|
||||
ASTNode parent = node.getParent() != null ? node.getParent().getParent() : null;
|
||||
while (parent != null && !(parent instanceof TypeDeclaration)) {
|
||||
parent = parent.getParent();
|
||||
}
|
||||
|
||||
if (parent != null) {
|
||||
TypeDeclaration type = (TypeDeclaration) parent;
|
||||
ITypeBinding typeBinding = type.resolveBinding();
|
||||
if (typeBinding != null) {
|
||||
return findFirstRequestMappingAnnotation(typeBinding);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private IAnnotationBinding findFirstRequestMappingAnnotation(ITypeBinding start) {
|
||||
if (start == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
IAnnotationBinding found = getRequestMappingAnnotation(start);
|
||||
if (found != null) {
|
||||
return found;
|
||||
}
|
||||
else {
|
||||
// search interfaces first
|
||||
ITypeBinding[] interfaces = start.getInterfaces();
|
||||
if (interfaces != null) {
|
||||
for (int i = 0; i < interfaces.length; i++) {
|
||||
found = findFirstRequestMappingAnnotation(interfaces[i]);
|
||||
if (found != null) {
|
||||
return found;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// search superclass second
|
||||
ITypeBinding superclass = start.getSuperclass();
|
||||
if (superclass != null) {
|
||||
return findFirstRequestMappingAnnotation(superclass);
|
||||
}
|
||||
|
||||
// nothing found
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private IAnnotationBinding getRequestMappingAnnotation(ITypeBinding typeBinding) {
|
||||
IAnnotationBinding[] annotations = typeBinding.getAnnotations();
|
||||
for (int i = 0; i < annotations.length; i++) {
|
||||
if (annotations[i].getAnnotationType() != null && Annotations.SPRING_REQUEST_MAPPING.equals(annotations[i].getAnnotationType().getQualifiedName())) {
|
||||
return annotations[i];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -35,9 +35,8 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.ide.vscode.boot.java.utils.ASTUtils;
|
||||
import org.springframework.ide.vscode.boot.java.utils.CachedSymbol;
|
||||
import org.springframework.ide.vscode.boot.java.utils.SpringIndexerJava.SCAN_PASS;
|
||||
import org.springframework.ide.vscode.boot.java.utils.SpringIndexerJavaContext;
|
||||
import org.springframework.ide.vscode.commons.protocol.spring.SpringIndexElement;
|
||||
import org.springframework.ide.vscode.commons.protocol.spring.Bean;
|
||||
import org.springframework.ide.vscode.commons.util.BadLocationException;
|
||||
import org.springframework.ide.vscode.commons.util.text.TextDocument;
|
||||
|
||||
@@ -59,36 +58,34 @@ public class WebfluxRouterSymbolProvider {
|
||||
return false;
|
||||
}
|
||||
|
||||
public static SpringIndexElement[] createWebfluxElements(MethodDeclaration methodDeclaration, SpringIndexerJavaContext context, TextDocument doc, List<SpringIndexElement> childElements) {
|
||||
public static void createWebfluxElements(Bean beanDefinition, MethodDeclaration methodDeclaration, SpringIndexerJavaContext context, TextDocument doc) {
|
||||
Block methodBody = methodDeclaration.getBody();
|
||||
if (methodBody != null && methodBody.statements() != null && methodBody.statements().size() > 0) {
|
||||
addSymbolsForRouterFunction(methodBody, context, doc, childElements);
|
||||
}
|
||||
|
||||
return new SpringIndexElement[0];
|
||||
}
|
||||
|
||||
public void addSymbols(MethodDeclaration methodDeclaration, SpringIndexerJavaContext context, TextDocument doc) {
|
||||
Type returnType = methodDeclaration.getReturnType2();
|
||||
if (returnType != null) {
|
||||
|
||||
ITypeBinding resolvedBinding = returnType.resolveBinding();
|
||||
|
||||
if (resolvedBinding != null && WebfluxUtils.ROUTER_FUNCTION_TYPE.equals(resolvedBinding.getBinaryName())) {
|
||||
|
||||
Block methodBody = methodDeclaration.getBody();
|
||||
if (methodBody != null && methodBody.statements() != null && methodBody.statements().size() > 0) {
|
||||
addSymbolsForRouterFunction(methodBody, context, doc, new ArrayList<>());
|
||||
}
|
||||
else if (SCAN_PASS.ONE.equals(context.getPass())) {
|
||||
context.getNextPassFiles().add(context.getFile());
|
||||
}
|
||||
|
||||
}
|
||||
addSymbolsForRouterFunction(beanDefinition, methodBody, context, doc);
|
||||
}
|
||||
}
|
||||
|
||||
private static void addSymbolsForRouterFunction(Block methodBody, SpringIndexerJavaContext context, TextDocument doc, List<SpringIndexElement> indexElementsCollector) {
|
||||
// public void addSymbols(MethodDeclaration methodDeclaration, SpringIndexerJavaContext context, TextDocument doc) {
|
||||
// Type returnType = methodDeclaration.getReturnType2();
|
||||
// if (returnType != null) {
|
||||
//
|
||||
// ITypeBinding resolvedBinding = returnType.resolveBinding();
|
||||
//
|
||||
// if (resolvedBinding != null && WebfluxUtils.ROUTER_FUNCTION_TYPE.equals(resolvedBinding.getBinaryName())) {
|
||||
//
|
||||
// Block methodBody = methodDeclaration.getBody();
|
||||
// if (methodBody != null && methodBody.statements() != null && methodBody.statements().size() > 0) {
|
||||
// addSymbolsForRouterFunction(methodBody, context, doc, new ArrayList<>());
|
||||
// }
|
||||
// else if (SCAN_PASS.ONE.equals(context.getPass())) {
|
||||
// context.getNextPassFiles().add(context.getFile());
|
||||
// }
|
||||
//
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
private static void addSymbolsForRouterFunction(Bean beanDefinition, Block methodBody, SpringIndexerJavaContext context, TextDocument doc) {
|
||||
methodBody.accept(new ASTVisitor() {
|
||||
|
||||
@Override
|
||||
@@ -96,7 +93,7 @@ public class WebfluxRouterSymbolProvider {
|
||||
IMethodBinding methodBinding = node.resolveMethodBinding();
|
||||
|
||||
if (methodBinding != null && WebfluxUtils.isRouteMethodInvocation(methodBinding)) {
|
||||
extractMappingSymbol(node, doc, context, indexElementsCollector);
|
||||
extractMappingSymbol(beanDefinition, node, doc, context);
|
||||
}
|
||||
|
||||
return super.visit(node);
|
||||
@@ -105,7 +102,7 @@ public class WebfluxRouterSymbolProvider {
|
||||
});
|
||||
}
|
||||
|
||||
protected static void extractMappingSymbol(MethodInvocation node, TextDocument doc, SpringIndexerJavaContext context, List<SpringIndexElement> indexElementsCollector) {
|
||||
protected static void extractMappingSymbol(Bean beanDefinition, MethodInvocation node, TextDocument doc, SpringIndexerJavaContext context) {
|
||||
WebfluxRouteElement[] pathElements = extractPath(node, doc);
|
||||
WebfluxRouteElement[] httpMethods = extractMethods(node, doc);
|
||||
WebfluxRouteElement[] contentTypes = extractContentTypes(node, doc);
|
||||
@@ -134,8 +131,8 @@ public class WebfluxRouterSymbolProvider {
|
||||
WebfluxHandlerMethodIndexElement handler = extractHandlerInformation(node, path, httpMethods, contentTypes, acceptTypes, location.getRange(), symbol.getName());
|
||||
WebfluxRouteElementRangesIndexElement elements = extractElementsInformation(pathElements, httpMethods, contentTypes, acceptTypes);
|
||||
|
||||
if (handler != null) indexElementsCollector.add(handler);
|
||||
if (elements != null) indexElementsCollector.add(elements);
|
||||
if (handler != null) beanDefinition.addChild(handler);
|
||||
if (elements != null) beanDefinition.addChild(elements);
|
||||
|
||||
} catch (BadLocationException e) {
|
||||
log.error("bad location while extracting mapping symbol for " + doc.getUri(), e);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2018 Pivotal, Inc.
|
||||
* Copyright (c) 2018, 2025 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
|
||||
@@ -15,6 +15,7 @@ import org.eclipse.lsp4j.Location;
|
||||
import org.eclipse.lsp4j.SymbolKind;
|
||||
import org.eclipse.lsp4j.WorkspaceSymbol;
|
||||
import org.eclipse.lsp4j.jsonrpc.messages.Either;
|
||||
import org.springframework.ide.vscode.commons.util.BadLocationException;
|
||||
import org.springframework.ide.vscode.commons.util.text.TextDocument;
|
||||
|
||||
/**
|
||||
@@ -22,7 +23,7 @@ import org.springframework.ide.vscode.commons.util.text.TextDocument;
|
||||
*/
|
||||
public class DefaultSymbolProvider {
|
||||
|
||||
public static WorkspaceSymbol provideDefaultSymbol(Annotation node, TextDocument doc) throws Exception {
|
||||
public static WorkspaceSymbol provideDefaultSymbol(Annotation node, TextDocument doc) throws BadLocationException {
|
||||
WorkspaceSymbol symbol = new WorkspaceSymbol(node.toString(), SymbolKind.Interface,
|
||||
Either.forLeft(new Location(doc.getUri(), doc.toRange(node.getStartPosition(), node.getLength()))));
|
||||
return symbol;
|
||||
|
||||
@@ -22,29 +22,37 @@ import org.eclipse.lsp4j.WorkspaceSymbol;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.ide.vscode.boot.java.Annotations;
|
||||
import org.springframework.ide.vscode.boot.java.beans.CachedBean;
|
||||
import org.springframework.ide.vscode.boot.java.handlers.AbstractSymbolProvider;
|
||||
import org.springframework.ide.vscode.commons.protocol.spring.SimpleSymbolElement;
|
||||
import org.springframework.ide.vscode.boot.java.handlers.SymbolProvider;
|
||||
import org.springframework.ide.vscode.commons.util.BadLocationException;
|
||||
import org.springframework.ide.vscode.commons.util.text.TextDocument;
|
||||
|
||||
/**
|
||||
* @author Martin Lippert
|
||||
*/
|
||||
public class RestrictedDefaultSymbolProvider extends AbstractSymbolProvider {
|
||||
public class RestrictedDefaultSymbolProvider implements SymbolProvider {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(RestrictedDefaultSymbolProvider.class);
|
||||
|
||||
@Override
|
||||
protected void addSymbolsPass1(Annotation node, ITypeBinding typeBinding,
|
||||
Collection<ITypeBinding> metaAnnotations, SpringIndexerJavaContext context, TextDocument doc) {
|
||||
public void addSymbols(Annotation node, ITypeBinding typeBinding, Collection<ITypeBinding> metaAnnotations, SpringIndexerJavaContext context, TextDocument doc) {
|
||||
|
||||
// provide default symbol only in case this annotation is not combined with @Bean annotation
|
||||
if (!isCombinedWithAnnotation(node, Annotations.BEAN)) {
|
||||
try {
|
||||
WorkspaceSymbol symbol = DefaultSymbolProvider.provideDefaultSymbol(node, doc);
|
||||
context.getGeneratedSymbols().add(new CachedSymbol(context.getDocURI(), context.getLastModified(), symbol));
|
||||
context.getBeans().add(new CachedBean(context.getDocURI(), new SimpleSymbolElement(symbol)));
|
||||
} catch (Exception e) {
|
||||
|
||||
// SimpleSymbolElement symbolIndexElement = new SimpleSymbolElement(symbol);
|
||||
// SpringIndexElement parentIndexElement = context.getNearestIndexElementForNode(node.getParent());
|
||||
// if (parentIndexElement != null) {
|
||||
// parentIndexElement.addChild(symbolIndexElement);
|
||||
// }
|
||||
// else {
|
||||
// context.getBeans().add(new CachedBean(context.getDocURI(), symbolIndexElement));
|
||||
// }
|
||||
// context.setIndexElementForASTNode(node.getParent(), symbolIndexElement);
|
||||
|
||||
} catch (BadLocationException e) {
|
||||
log.warn(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ import org.slf4j.LoggerFactory;
|
||||
import org.springframework.ide.vscode.boot.index.cache.IndexCache;
|
||||
import org.springframework.ide.vscode.boot.index.cache.IndexCacheKey;
|
||||
import org.springframework.ide.vscode.boot.java.beans.BeanUtils;
|
||||
import org.springframework.ide.vscode.boot.java.beans.BeansSymbolProvider;
|
||||
import org.springframework.ide.vscode.boot.java.beans.BeansIndexer;
|
||||
import org.springframework.ide.vscode.commons.java.IClasspathUtil;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
import org.springframework.ide.vscode.commons.protocol.java.Classpath;
|
||||
@@ -142,7 +142,7 @@ public class SpringFactoriesIndexer implements SpringIndexer {
|
||||
String beanId = BeanUtils.getBeanNameFromType(simpleName);
|
||||
Range range = doc.toRange(new Region(pair.getOffset(), pair.getLength()));
|
||||
symbols.add(new WorkspaceSymbol(
|
||||
BeansSymbolProvider.beanLabel(false, beanId, fqName, Paths.get(URI.create(docURI)).getFileName().toString()),
|
||||
BeansIndexer.beanLabel(false, beanId, fqName, Paths.get(URI.create(docURI)).getFileName().toString()),
|
||||
SymbolKind.Interface,
|
||||
Either.forLeft(new Location(docURI, range))));
|
||||
} catch (Exception e) {
|
||||
|
||||
@@ -73,7 +73,6 @@ import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFin
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.IProblemCollector;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.ReconcileProblem;
|
||||
import org.springframework.ide.vscode.commons.protocol.java.Classpath;
|
||||
import org.springframework.ide.vscode.commons.protocol.spring.SimpleSymbolElement;
|
||||
import org.springframework.ide.vscode.commons.protocol.spring.SpringIndexElement;
|
||||
import org.springframework.ide.vscode.commons.util.UriUtil;
|
||||
import org.springframework.ide.vscode.commons.util.text.TextDocument;
|
||||
@@ -87,10 +86,6 @@ import com.google.gson.JsonObject;
|
||||
*/
|
||||
public class SpringIndexerJava implements SpringIndexer {
|
||||
|
||||
public static enum SCAN_PASS {
|
||||
ONE, TWO
|
||||
}
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(SpringIndexerJava.class);
|
||||
|
||||
// whenever the implementation of the indexer changes in a way that the stored data in the cache is no longer valid,
|
||||
@@ -308,7 +303,7 @@ public class SpringIndexerJava implements SpringIndexer {
|
||||
IProblemCollector problemCollector = problemCollectorCreator.apply(docRef, diagnosticsAggregator);
|
||||
|
||||
SpringIndexerJavaContext context = new SpringIndexerJavaContext(project, cu, docURI, file,
|
||||
lastModified, docRef, content, generatedSymbols, generatedBeans, problemCollector, SCAN_PASS.ONE, new ArrayList<>(), !ignoreMethodBodies);
|
||||
lastModified, docRef, content, generatedSymbols, generatedBeans, problemCollector, new ArrayList<>(), !ignoreMethodBodies);
|
||||
|
||||
scanAST(context, true);
|
||||
|
||||
@@ -359,7 +354,7 @@ public class SpringIndexerJava implements SpringIndexer {
|
||||
AtomicReference<TextDocument> docRef = new AtomicReference<>();
|
||||
String file = UriUtil.toFileString(docURI);
|
||||
SpringIndexerJavaContext context = new SpringIndexerJavaContext(project, cu, docURI, file,
|
||||
0, docRef, content, generatedSymbols, generatedBeans, voidProblemCollector, SCAN_PASS.ONE, new ArrayList<>(), true);
|
||||
0, docRef, content, generatedSymbols, generatedBeans, voidProblemCollector, new ArrayList<>(), true);
|
||||
|
||||
scanAST(context, false);
|
||||
|
||||
@@ -396,7 +391,7 @@ public class SpringIndexerJava implements SpringIndexer {
|
||||
AtomicReference<TextDocument> docRef = new AtomicReference<>();
|
||||
String file = UriUtil.toFileString(docURI);
|
||||
SpringIndexerJavaContext context = new SpringIndexerJavaContext(project, cu, docURI, file,
|
||||
0, docRef, content, generatedSymbols, generatedBeans, voidProblemCollector, SCAN_PASS.ONE, new ArrayList<>(), true);
|
||||
0, docRef, content, generatedSymbols, generatedBeans, voidProblemCollector, new ArrayList<>(), true);
|
||||
|
||||
scanAST(context, false);
|
||||
|
||||
@@ -457,7 +452,7 @@ public class SpringIndexerJava implements SpringIndexer {
|
||||
IProblemCollector problemCollector = problemCollectorCreator.apply(docRef, diagnosticsAggregator);
|
||||
|
||||
SpringIndexerJavaContext context = new SpringIndexerJavaContext(project, cu, docURI, sourceFilePath,
|
||||
lastModified, docRef, null, generatedSymbols, generatedBeans, problemCollector, SCAN_PASS.ONE, new ArrayList<>(), !ignoreMethodBodies);
|
||||
lastModified, docRef, null, generatedSymbols, generatedBeans, problemCollector, new ArrayList<>(), !ignoreMethodBodies);
|
||||
|
||||
scanAST(context, true);
|
||||
|
||||
@@ -560,11 +555,11 @@ public class SpringIndexerJava implements SpringIndexer {
|
||||
for (int i = 0; i < chunks.size(); i++) {
|
||||
|
||||
log.info("scan java files, AST parse, chunk {} for files: {}", i, javaFiles.length);
|
||||
String[] pass2Files = scanFiles(project, annotations, chunks.get(i), generatedSymbols, generatedBeans, diagnosticsAggregator, SCAN_PASS.ONE);
|
||||
String[] pass2Files = scanFiles(project, annotations, chunks.get(i), generatedSymbols, generatedBeans, diagnosticsAggregator, true);
|
||||
|
||||
if (pass2Files.length > 0) {
|
||||
log.info("scan java files, AST parse, pass 2, chunk {} for files: {}", i, javaFiles.length);
|
||||
scanFiles(project, annotations, pass2Files, generatedSymbols, generatedBeans, diagnosticsAggregator, SCAN_PASS.TWO);
|
||||
scanFiles(project, annotations, pass2Files, generatedSymbols, generatedBeans, diagnosticsAggregator, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -602,13 +597,12 @@ public class SpringIndexerJava implements SpringIndexer {
|
||||
}
|
||||
|
||||
private String[] scanFiles(IJavaProject project, AnnotationHierarchies annotations, String[] javaFiles, List<CachedSymbol> generatedSymbols, List<CachedBean> generatedBeans,
|
||||
BiConsumer<String, Diagnostic> diagnosticsAggregator, SCAN_PASS pass) throws Exception {
|
||||
BiConsumer<String, Diagnostic> diagnosticsAggregator, boolean ignoreMethodBodies) throws Exception {
|
||||
|
||||
PercentageProgressTask progressTask = this.progressService.createPercentageProgressTask(INDEX_FILES_TASK_ID + project.getElementName(),
|
||||
javaFiles.length, "Spring Tools: Indexing Java Sources for '" + project.getElementName() + "'");
|
||||
|
||||
List<String> nextPassFiles = new ArrayList<>();
|
||||
final boolean ignoreMethodBodies = SCAN_PASS.ONE.equals(pass);
|
||||
|
||||
FileASTRequestor requestor = new FileASTRequestor() {
|
||||
|
||||
@@ -622,7 +616,7 @@ public class SpringIndexerJava implements SpringIndexer {
|
||||
IProblemCollector problemCollector = problemCollectorCreator.apply(docRef, diagnosticsAggregator);
|
||||
|
||||
SpringIndexerJavaContext context = new SpringIndexerJavaContext(project, cu, docURI, sourceFilePath,
|
||||
lastModified, docRef, null, generatedSymbols, generatedBeans, problemCollector, pass, nextPassFiles, !ignoreMethodBodies);
|
||||
lastModified, docRef, null, generatedSymbols, generatedBeans, problemCollector, nextPassFiles, !ignoreMethodBodies);
|
||||
|
||||
scanAST(context, true);
|
||||
progressTask.increment();
|
||||
@@ -655,69 +649,95 @@ public class SpringIndexerJava implements SpringIndexer {
|
||||
}
|
||||
|
||||
private void scanAST(final SpringIndexerJavaContext context, boolean includeReconcile) {
|
||||
context.getCu().accept(new ASTVisitor() {
|
||||
|
||||
@Override
|
||||
public boolean visit(TypeDeclaration node) {
|
||||
try {
|
||||
context.addScannedType(node.resolveBinding());
|
||||
extractSymbolInformation(node, context);
|
||||
try {
|
||||
context.getCu().accept(new ASTVisitor() {
|
||||
|
||||
@Override
|
||||
public boolean visit(TypeDeclaration node) {
|
||||
try {
|
||||
context.addScannedType(node.resolveBinding());
|
||||
extractSymbolInformation(node, context);
|
||||
}
|
||||
catch (RequiredCompleteAstException e) {
|
||||
throw e;
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.error("error extracting symbol information in project '" + context.getProject().getElementName() + "' - for docURI '" + context.getDocURI() + "' - on node: " + node.toString(), e);
|
||||
}
|
||||
|
||||
return super.visit(node);
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.error("error extracting symbol information in project '" + context.getProject().getElementName() + "' - for docURI '" + context.getDocURI() + "' - on node: " + node.toString(), e);
|
||||
|
||||
@Override
|
||||
public boolean visit(MethodDeclaration node) {
|
||||
try {
|
||||
extractSymbolInformation(node, context);
|
||||
}
|
||||
catch (RequiredCompleteAstException e) {
|
||||
throw e;
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.error("error extracting symbol information in project '" + context.getProject().getElementName() + "' - for docURI '" + context.getDocURI() + "' - on node: " + node.toString(), e);
|
||||
}
|
||||
return super.visit(node);
|
||||
}
|
||||
|
||||
return super.visit(node);
|
||||
|
||||
@Override
|
||||
public boolean visit(SingleMemberAnnotation node) {
|
||||
try {
|
||||
extractSymbolInformation(node, context);
|
||||
}
|
||||
catch (RequiredCompleteAstException e) {
|
||||
throw e;
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.error("error extracting symbol information in project '" + context.getProject().getElementName() + "' - for docURI '" + context.getDocURI() + "' - on node: " + node.toString(), e);
|
||||
}
|
||||
|
||||
return super.visit(node);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(NormalAnnotation node) {
|
||||
try {
|
||||
extractSymbolInformation(node, context);
|
||||
}
|
||||
catch (RequiredCompleteAstException e) {
|
||||
throw e;
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.error("error extracting symbol information in project '" + context.getProject().getElementName() + "' - for docURI '" + context.getDocURI() + "' - on node: " + node.toString(), e);
|
||||
}
|
||||
|
||||
return super.visit(node);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(MarkerAnnotation node) {
|
||||
try {
|
||||
extractSymbolInformation(node, context);
|
||||
}
|
||||
catch (RequiredCompleteAstException e) {
|
||||
throw e;
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.error("error extracting symbol information in project '" + context.getProject().getElementName() + "' - for docURI '" + context.getDocURI() + "' - on node: " + node.toString(), e);
|
||||
}
|
||||
|
||||
return super.visit(node);
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (RequiredCompleteAstException e) {
|
||||
if (!context.isFullAst()) {
|
||||
context.getNextPassFiles().add(context.getFile());
|
||||
context.resetDocumentRelatedElements(context.getDocURI());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(MethodDeclaration node) {
|
||||
try {
|
||||
extractSymbolInformation(node, context);
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.error("error extracting symbol information in project '" + context.getProject().getElementName() + "' - for docURI '" + context.getDocURI() + "' - on node: " + node.toString(), e);
|
||||
}
|
||||
return super.visit(node);
|
||||
else {
|
||||
log.error("Complete AST required but it is complete already. Analyzing ", context.getDocURI());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(SingleMemberAnnotation node) {
|
||||
try {
|
||||
extractSymbolInformation(node, context);
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.error("error extracting symbol information in project '" + context.getProject().getElementName() + "' - for docURI '" + context.getDocURI() + "' - on node: " + node.toString(), e);
|
||||
}
|
||||
|
||||
return super.visit(node);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(NormalAnnotation node) {
|
||||
try {
|
||||
extractSymbolInformation(node, context);
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.error("error extracting symbol information in project '" + context.getProject().getElementName() + "' - for docURI '" + context.getDocURI() + "' - on node: " + node.toString(), e);
|
||||
}
|
||||
|
||||
return super.visit(node);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(MarkerAnnotation node) {
|
||||
try {
|
||||
extractSymbolInformation(node, context);
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.error("error extracting symbol information in project '" + context.getProject().getElementName() + "' - for docURI '" + context.getDocURI() + "' - on node: " + node.toString(), e);
|
||||
}
|
||||
|
||||
return super.visit(node);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
if (includeReconcile) {
|
||||
reconcile(context);
|
||||
}
|
||||
@@ -754,12 +774,12 @@ public class SpringIndexerJava implements SpringIndexer {
|
||||
reconciler.reconcile(context.getProject(), URI.create(context.getDocURI()), context.getCu(), problemCollector, context.isFullAst());
|
||||
problemCollector.endCollecting();
|
||||
} catch (RequiredCompleteAstException e) {
|
||||
if (context.getPass() == SCAN_PASS.TWO) {
|
||||
problemCollector.endCollecting();
|
||||
log.error("Complete AST required but it is complete already. Parsing ", context.getDocURI());
|
||||
} else {
|
||||
if (!context.isFullAst()) {
|
||||
// Let problems be found in the next pass, don't add the problems to the aggregate problems collector to not duplicate them with the next pass
|
||||
context.getNextPassFiles().add(context.getFile());
|
||||
} else {
|
||||
problemCollector.endCollecting();
|
||||
log.error("Complete AST required but it is complete already. Parsing ", context.getDocURI());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -814,7 +834,16 @@ public class SpringIndexerJava implements SpringIndexer {
|
||||
WorkspaceSymbol symbol = provideDefaultSymbol(node, context);
|
||||
if (symbol != null) {
|
||||
context.getGeneratedSymbols().add(new CachedSymbol(context.getDocURI(), context.getLastModified(), symbol));
|
||||
context.getBeans().add(new CachedBean(context.getDocURI(), new SimpleSymbolElement(symbol)));
|
||||
|
||||
// SimpleSymbolElement symbolIndexElement = new SimpleSymbolElement(symbol);
|
||||
// SpringIndexElement parentIndexElement = context.getNearestIndexElementForNode(node.getParent());
|
||||
// if (parentIndexElement != null) {
|
||||
// parentIndexElement.addChild(symbolIndexElement);
|
||||
// }
|
||||
// else {
|
||||
// context.getBeans().add(new CachedBean(context.getDocURI(), symbolIndexElement));
|
||||
// }
|
||||
// context.setIndexElementForASTNode(node.getParent(), symbolIndexElement);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -837,6 +866,9 @@ public class SpringIndexerJava implements SpringIndexer {
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (RequiredCompleteAstException e) {
|
||||
throw e;
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.error("error creating default symbol in project '" + context.getProject().getElementName() + "' - for docURI '" + context.getDocURI() + "' - on node: " + node.toString(), e);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017, 2023 Pivotal, Inc.
|
||||
* Copyright (c) 2017, 2025 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
|
||||
@@ -11,6 +11,7 @@
|
||||
package org.springframework.ide.vscode.boot.java.utils;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
@@ -18,7 +19,6 @@ import java.util.concurrent.atomic.AtomicReference;
|
||||
import org.eclipse.jdt.core.dom.CompilationUnit;
|
||||
import org.eclipse.jdt.core.dom.ITypeBinding;
|
||||
import org.springframework.ide.vscode.boot.java.beans.CachedBean;
|
||||
import org.springframework.ide.vscode.boot.java.utils.SpringIndexerJava.SCAN_PASS;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.IProblemCollector;
|
||||
import org.springframework.ide.vscode.commons.util.text.TextDocument;
|
||||
@@ -38,14 +38,12 @@ public class SpringIndexerJavaContext {
|
||||
private final List<CachedSymbol> generatedSymbols;
|
||||
private final List<CachedBean> beans;
|
||||
private final IProblemCollector getProblemCollector;
|
||||
private final SCAN_PASS pass;
|
||||
private final List<String> nextPassFiles;
|
||||
private final boolean fullAst;
|
||||
|
||||
private final Set<String> dependencies = new HashSet<>();
|
||||
private final Set<String> scannedTypes = new HashSet<>();
|
||||
|
||||
|
||||
|
||||
public SpringIndexerJavaContext(
|
||||
IJavaProject project,
|
||||
CompilationUnit cu,
|
||||
@@ -57,7 +55,6 @@ public class SpringIndexerJavaContext {
|
||||
List<CachedSymbol> generatedSymbols,
|
||||
List<CachedBean> beans,
|
||||
IProblemCollector problemCollector,
|
||||
SCAN_PASS pass,
|
||||
List<String> nextPassFiles,
|
||||
boolean fullAst
|
||||
) {
|
||||
@@ -72,7 +69,6 @@ public class SpringIndexerJavaContext {
|
||||
this.generatedSymbols = generatedSymbols;
|
||||
this.getProblemCollector = problemCollector;
|
||||
this.beans = beans;
|
||||
this.pass = pass;
|
||||
this.nextPassFiles = nextPassFiles;
|
||||
this.fullAst = fullAst;
|
||||
}
|
||||
@@ -113,10 +109,6 @@ public class SpringIndexerJavaContext {
|
||||
return beans;
|
||||
}
|
||||
|
||||
public SCAN_PASS getPass() {
|
||||
return pass;
|
||||
}
|
||||
|
||||
public List<String> getNextPassFiles() {
|
||||
return nextPassFiles;
|
||||
}
|
||||
@@ -154,4 +146,22 @@ public class SpringIndexerJavaContext {
|
||||
public boolean isFullAst() {
|
||||
return fullAst;
|
||||
}
|
||||
|
||||
public void resetDocumentRelatedElements(String docURI) {
|
||||
Iterator<CachedBean> beansIterator = beans.iterator();
|
||||
while (beansIterator.hasNext()) {
|
||||
if (beansIterator.next().getDocURI().equals(docURI)) {
|
||||
beansIterator.remove();
|
||||
}
|
||||
}
|
||||
|
||||
Iterator<CachedSymbol> symbolsIterator = generatedSymbols.iterator();
|
||||
while (symbolsIterator.hasNext()) {
|
||||
if (symbolsIterator.next().getDocURI().equals(docURI)) {
|
||||
symbolsIterator.remove();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,148 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2025 Broadcom
|
||||
* 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:
|
||||
* Broadcom - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.index.test;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertSame;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.lsp4j.TextDocumentIdentifier;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Import;
|
||||
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.requestmapping.RequestMappingIndexElement;
|
||||
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.DocumentElement;
|
||||
import org.springframework.ide.vscode.commons.protocol.spring.SimpleSymbolElement;
|
||||
import org.springframework.ide.vscode.commons.protocol.spring.SpringIndexElement;
|
||||
import org.springframework.ide.vscode.project.harness.BootLanguageServerHarness;
|
||||
import org.springframework.ide.vscode.project.harness.ProjectsHarness;
|
||||
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||
|
||||
/**
|
||||
* @author Martin Lippert
|
||||
*/
|
||||
@ExtendWith(SpringExtension.class)
|
||||
@BootLanguageServerTest
|
||||
@Import(SymbolProviderTestConf.class)
|
||||
public class SpringIndexStructureTest {
|
||||
|
||||
@Autowired private BootLanguageServerHarness harness;
|
||||
@Autowired private JavaProjectFinder projectFinder;
|
||||
@Autowired private SpringMetamodelIndex springIndex;
|
||||
@Autowired private SpringSymbolIndex indexer;
|
||||
|
||||
private static final String PROJECT_NAME = "test-spring-index-structure";
|
||||
|
||||
private File directory;
|
||||
|
||||
@BeforeEach
|
||||
public void setup() throws Exception {
|
||||
harness.intialize(null);
|
||||
|
||||
directory = new File(ProjectsHarness.class.getResource("/test-projects/" + PROJECT_NAME + "/").toURI());
|
||||
|
||||
String projectDir = directory.toURI().toString();
|
||||
|
||||
// trigger project creation
|
||||
projectFinder.find(new TextDocumentIdentifier(projectDir)).get();
|
||||
|
||||
CompletableFuture<Void> initProject = indexer.waitOperation();
|
||||
initProject.get(55555555, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
void testStructureMainClass() {
|
||||
String docUri = directory.toPath().resolve("src/main/java/org/test/MainClass.java").toUri().toString();
|
||||
|
||||
/*
|
||||
* @SpringBootApplication (mainClass) - bean node
|
||||
* - @Value("server.port") - default node
|
||||
* - @Bean (bean1) - bean node
|
||||
*/
|
||||
|
||||
// document node
|
||||
DocumentElement document = springIndex.getDocument(docUri);
|
||||
List<SpringIndexElement> docChildren = document.getChildren();
|
||||
assertEquals(1, docChildren.size());
|
||||
|
||||
// mainClass - bean node
|
||||
Bean[] mainClassBean = springIndex.getBeansWithName(PROJECT_NAME, "mainClass");
|
||||
assertEquals(1, mainClassBean.length);
|
||||
assertEquals("mainClass", mainClassBean[0].getName());
|
||||
assertSame(mainClassBean[0], docChildren.get(0));
|
||||
|
||||
// mainClass children
|
||||
List<SpringIndexElement> mainChildren = mainClassBean[0].getChildren();
|
||||
assertEquals(2, mainChildren.size());
|
||||
|
||||
SpringIndexElement valueFieldNode = mainChildren.get(0);
|
||||
SpringIndexElement beanMethodNode = mainChildren.get(1);
|
||||
|
||||
assertTrue(valueFieldNode instanceof SimpleSymbolElement);
|
||||
assertTrue(beanMethodNode instanceof Bean);
|
||||
|
||||
SimpleSymbolElement valueField = (SimpleSymbolElement) valueFieldNode;
|
||||
Bean beanMethod = (Bean) beanMethodNode;
|
||||
|
||||
assertEquals("bean1", beanMethod.getName());
|
||||
assertEquals("@Value(\"server.port\")", valueField.getDocumentSymbol().getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testStructureRestController() {
|
||||
String docUri = directory.toPath().resolve("src/main/java/org/test/RestControllerExample.java").toUri().toString();
|
||||
|
||||
/*
|
||||
* @RestController (mainClass) - bean node
|
||||
* - /owners/find -- GET -> @GetMapping("/owners/find")
|
||||
* - /owners/new -- POST -> @PostMapping("/owners/new")
|
||||
*/
|
||||
|
||||
// document node
|
||||
DocumentElement document = springIndex.getDocument(docUri);
|
||||
List<SpringIndexElement> docChildren = document.getChildren();
|
||||
assertEquals(1, docChildren.size());
|
||||
|
||||
// rest controller node
|
||||
Bean[] mainClassBean = springIndex.getBeansWithName(PROJECT_NAME, "restControllerExample");
|
||||
assertEquals(1, mainClassBean.length);
|
||||
assertEquals("restControllerExample", mainClassBean[0].getName());
|
||||
assertSame(mainClassBean[0], docChildren.get(0));
|
||||
|
||||
// rest controller children
|
||||
List<SpringIndexElement> mainChildren = mainClassBean[0].getChildren();
|
||||
assertEquals(3, mainChildren.size());
|
||||
|
||||
RequestMappingIndexElement getMappingNode = (RequestMappingIndexElement) mainChildren.get(0);
|
||||
RequestMappingIndexElement postMappingNode = (RequestMappingIndexElement) mainChildren.get(1);
|
||||
RequestMappingIndexElement genericMappingNode = (RequestMappingIndexElement) mainChildren.get(2);
|
||||
|
||||
assertEquals("/owners/find", getMappingNode.getPath());
|
||||
assertEquals("/owners/new", postMappingNode.getPath());
|
||||
assertEquals("/owners/{ownerId}/edit", genericMappingNode.getPath());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -190,10 +190,10 @@ public class SpringIndexerEventsTest {
|
||||
Bean[] beans = springIndex.getBeansOfDocument(docUri);
|
||||
assertEquals(1, beans.length);
|
||||
|
||||
Bean listenerComponentBean = Arrays.stream(beans).filter(bean -> bean.getName().equals("customEventPublisher")).findFirst().get();
|
||||
assertEquals("com.example.events.demo.CustomEventPublisher", listenerComponentBean.getType());
|
||||
Bean publisherComponentBean = Arrays.stream(beans).filter(bean -> bean.getName().equals("customEventPublisher")).findFirst().get();
|
||||
assertEquals("com.example.events.demo.CustomEventPublisher", publisherComponentBean.getType());
|
||||
|
||||
List<SpringIndexElement> children = listenerComponentBean.getChildren();
|
||||
List<SpringIndexElement> children = publisherComponentBean.getChildren();
|
||||
assertEquals(1, children.size());
|
||||
assertTrue(children.get(0) instanceof EventPublisherIndexElement);
|
||||
|
||||
@@ -226,4 +226,15 @@ public class SpringIndexerEventsTest {
|
||||
assertFalse(eventTypesFromHierarchy.contains("java.lang.String"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testEventPublisherWithMoreSymbols() throws Exception {
|
||||
String docUri = directory.toPath().resolve("src/main/java/com/example/events/demo/CustomEventPublisherWithAdditionalElements.java").toUri().toString();
|
||||
|
||||
SpringIndexerHarness.assertDocumentSymbols(indexer, docUri,
|
||||
SpringIndexerHarness.symbol("@Qualifier(\"qualifier\")", "@Qualifier(\"qualifier\")"),
|
||||
SpringIndexerHarness.symbol("@Component", "@+ 'customEventPublisherWithAdditionalElements' (@Component) CustomEventPublisherWithAdditionalElements"),
|
||||
SpringIndexerHarness.symbol("this.publisher.publishEvent(new CustomEvent())", "@EventPublisher (CustomEvent)")
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -85,7 +85,7 @@ public class RequestMappingDependentConstantChangedTest {
|
||||
String docUri = directory.resolve("src/main/java/org/test/SimpleMappingClassWithConstantInDifferentClass.java").toUri().toString();
|
||||
String constantsUri = directory.resolve("src/main/java/org/test/Constants.java").toUri().toString();
|
||||
List<? extends WorkspaceSymbol> symbols = indexer.getSymbols(docUri);
|
||||
assertEquals(1, symbols.size());
|
||||
assertEquals(2, symbols.size());
|
||||
assertSymbol(docUri, "@/path/from/constant", "@RequestMapping(Constants.REQUEST_MAPPING_PATH)");
|
||||
|
||||
TestFileScanListener fileScanListener = new TestFileScanListener();
|
||||
@@ -99,7 +99,7 @@ public class RequestMappingDependentConstantChangedTest {
|
||||
fileScanListener.assertScannedUri(docUri, 1);
|
||||
|
||||
symbols = indexer.getSymbols(docUri);
|
||||
assertSymbolCount(1, symbols);
|
||||
assertSymbolCount(2, symbols);
|
||||
assertSymbol(docUri, "@/changed-path", "@RequestMapping(Constants.REQUEST_MAPPING_PATH)");
|
||||
}
|
||||
|
||||
@@ -108,7 +108,7 @@ public class RequestMappingDependentConstantChangedTest {
|
||||
String docUri = directory.resolve("src/main/java/org/test/SimpleMappingClassWithConstantInDifferentClass.java").toUri().toString();
|
||||
String constantsUri = directory.resolve("src/main/java/org/test/Constants.java").toUri().toString();
|
||||
List<? extends WorkspaceSymbol> symbols = indexer.getSymbols(docUri);
|
||||
assertEquals(1, symbols.size());
|
||||
assertEquals(2, symbols.size());
|
||||
assertSymbol(docUri, "@/path/from/constant", "@RequestMapping(Constants.REQUEST_MAPPING_PATH)");
|
||||
|
||||
TestFileScanListener fileScanListener = new TestFileScanListener();
|
||||
@@ -122,7 +122,7 @@ public class RequestMappingDependentConstantChangedTest {
|
||||
fileScanListener.assertScannedUri(docUri, 1);
|
||||
|
||||
symbols = indexer.getSymbols(docUri);
|
||||
assertSymbolCount(1, symbols);
|
||||
assertSymbolCount(2, symbols);
|
||||
assertSymbol(docUri, "@/changed-path", "@RequestMapping(Constants.REQUEST_MAPPING_PATH)");
|
||||
}
|
||||
|
||||
@@ -132,14 +132,14 @@ public class RequestMappingDependentConstantChangedTest {
|
||||
String chainConstantsUri_2 = directory.resolve("src/main/java/org/test/ChainElement2.java").toUri().toString();
|
||||
|
||||
List<? extends WorkspaceSymbol> symbols = indexer.getSymbols(docUri);
|
||||
assertEquals(1, symbols.size());
|
||||
assertEquals(2, symbols.size());
|
||||
assertSymbol(docUri, "@/path/from/chain", "@RequestMapping(ChainElement1.MAPPING_PATH_1)");
|
||||
|
||||
replaceInFile(chainConstantsUri_2, "path/from/chain", "/changed-path");
|
||||
indexer.updateDocument(chainConstantsUri_2, null, "triggered by test code").get();
|
||||
|
||||
symbols = indexer.getSymbols(docUri);
|
||||
assertSymbolCount(1, symbols);
|
||||
assertSymbolCount(2, symbols);
|
||||
assertSymbol(docUri, "@/path/from/chain", "@RequestMapping(ChainElement1.MAPPING_PATH_1)");
|
||||
|
||||
// You would expect here that the symbol got updated from "path/from/chain" to the changed value "/changed-path",
|
||||
@@ -159,12 +159,12 @@ public class RequestMappingDependentConstantChangedTest {
|
||||
|
||||
{
|
||||
List<? extends WorkspaceSymbol> symbols = indexer.getSymbols(pingUri);
|
||||
assertSymbolCount(1, symbols);
|
||||
assertSymbolCount(2, symbols);
|
||||
assertSymbol(pingUri, "@/pong -- GET", "@GetMapping(PongConstantRequestMapping.PONG)");
|
||||
}
|
||||
{
|
||||
List<? extends WorkspaceSymbol> symbols = indexer.getSymbols(pongUri);
|
||||
assertSymbolCount(1, symbols);
|
||||
assertSymbolCount(2, symbols);
|
||||
assertSymbol(pongUri, "@/ping -- GET", "@GetMapping(PingConstantRequestMapping.PING)");
|
||||
}
|
||||
|
||||
@@ -173,12 +173,12 @@ public class RequestMappingDependentConstantChangedTest {
|
||||
|
||||
{
|
||||
List<? extends WorkspaceSymbol> symbols = indexer.getSymbols(pingUri);
|
||||
assertSymbolCount(1, symbols);
|
||||
assertSymbolCount(2, symbols);
|
||||
assertSymbol(pingUri, "@/pong -- GET", "@GetMapping(PongConstantRequestMapping.PONG)");
|
||||
}
|
||||
{
|
||||
List<? extends WorkspaceSymbol> symbols = indexer.getSymbols(pongUri);
|
||||
assertSymbolCount(1, symbols);
|
||||
assertSymbolCount(2, symbols);
|
||||
assertSymbol(pongUri, "@/changed -- GET", "@GetMapping(PingConstantRequestMapping.PING)");
|
||||
}
|
||||
}
|
||||
@@ -192,12 +192,12 @@ public class RequestMappingDependentConstantChangedTest {
|
||||
|
||||
{
|
||||
List<? extends WorkspaceSymbol> symbols = indexer.getSymbols(pingUri);
|
||||
assertSymbolCount(1, symbols);
|
||||
assertSymbolCount(2, symbols);
|
||||
assertSymbol(pingUri, "@/pong -- GET", "@GetMapping(PongConstantRequestMapping.PONG)");
|
||||
}
|
||||
{
|
||||
List<? extends WorkspaceSymbol> symbols = indexer.getSymbols(pongUri);
|
||||
assertSymbolCount(1, symbols);
|
||||
assertSymbolCount(2, symbols);
|
||||
assertSymbol(pongUri, "@/ping -- GET", "@GetMapping(PingConstantRequestMapping.PING)");
|
||||
}
|
||||
|
||||
@@ -206,12 +206,12 @@ public class RequestMappingDependentConstantChangedTest {
|
||||
|
||||
{
|
||||
List<? extends WorkspaceSymbol> symbols = indexer.getSymbols(pingUri);
|
||||
assertSymbolCount(1, symbols);
|
||||
assertSymbolCount(2, symbols);
|
||||
assertSymbol(pingUri, "@/pong -- GET", "@GetMapping(PongConstantRequestMapping.PONG)");
|
||||
}
|
||||
{
|
||||
List<? extends WorkspaceSymbol> symbols = indexer.getSymbols(pongUri);
|
||||
assertSymbolCount(1, symbols);
|
||||
assertSymbolCount(2, symbols);
|
||||
assertSymbol(pongUri, "@/changed -- GET", "@GetMapping(PingConstantRequestMapping.PING)");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,11 +79,15 @@ public class RequestMappingSymbolProviderTest {
|
||||
CompletableFuture<Void> initProject = indexer.waitOperation();
|
||||
initProject.get(5, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
private List<? extends WorkspaceSymbol> getSymbols(String docUri) {
|
||||
return indexer.getWorkspaceSymbolsFromSymbolIndex(docUri);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSimpleRequestMappingSymbol() throws Exception {
|
||||
String docUri = directory.toPath().resolve("src/main/java/org/test/SimpleMappingClass.java").toUri().toString();
|
||||
List<? extends WorkspaceSymbol> symbols = indexer.getSymbols(docUri);
|
||||
List<? extends WorkspaceSymbol> symbols = getSymbols(docUri);
|
||||
assertEquals(2, symbols.size());
|
||||
assertTrue(containsSymbol(symbols, "@/greeting", docUri, 8, 1, 8, 29));
|
||||
}
|
||||
@@ -110,9 +114,9 @@ public class RequestMappingSymbolProviderTest {
|
||||
void testSimpleRequestMappingSymbolFromConstantInDifferentClass() throws Exception {
|
||||
String docUri = directory.toPath().resolve("src/main/java/org/test/SimpleMappingClassWithConstantInDifferentClass.java").toUri().toString();
|
||||
String constantsUri = directory.toPath().resolve("src/main/java/org/test/Constants.java").toUri().toString();
|
||||
List<? extends WorkspaceSymbol> symbols = indexer.getSymbols(docUri);
|
||||
assertEquals(1, symbols.size());
|
||||
assertTrue(containsSymbol(symbols, "@/path/from/constant", docUri, 6, 1, 6, 48));
|
||||
List<? extends WorkspaceSymbol> symbols = getSymbols(docUri);
|
||||
assertEquals(2, symbols.size());
|
||||
assertTrue(containsSymbol(symbols, "@/path/from/constant", docUri, 8, 1, 8, 48));
|
||||
|
||||
//Verify whether dependency tracker logics works properly for this example.
|
||||
SpringIndexerJavaDependencyTracker dt = indexer.getJavaIndexer().getDependencyTracker();
|
||||
@@ -135,9 +139,9 @@ public class RequestMappingSymbolProviderTest {
|
||||
void testUpdateDocumentWithConstantFromDifferentClass() throws Exception {
|
||||
String docUri = directory.toPath().resolve("src/main/java/org/test/SimpleMappingClassWithConstantInDifferentClass.java").toUri().toString();
|
||||
String constantsUri = directory.toPath().resolve("src/main/java/org/test/Constants.java").toUri().toString();
|
||||
List<? extends WorkspaceSymbol> symbols = indexer.getSymbols(docUri);
|
||||
assertEquals(1, symbols.size());
|
||||
assertTrue(containsSymbol(symbols, "@/path/from/constant", docUri, 6, 1, 6, 48));
|
||||
List<? extends WorkspaceSymbol> symbols = getSymbols(docUri);
|
||||
assertEquals(2, symbols.size());
|
||||
assertTrue(containsSymbol(symbols, "@/path/from/constant", docUri, 8, 1, 8, 48));
|
||||
|
||||
//Verify whether dependency tracker logics works properly for this example.
|
||||
SpringIndexerJavaDependencyTracker dt = indexer.getJavaIndexer().getDependencyTracker();
|
||||
@@ -190,9 +194,9 @@ public class RequestMappingSymbolProviderTest {
|
||||
@Test
|
||||
void testSimpleRequestMappingSymbolFromConstantInSameClass() throws Exception {
|
||||
String docUri = directory.toPath().resolve("src/main/java/org/test/SimpleMappingClassWithConstantInSameClass.java").toUri().toString();
|
||||
List<? extends WorkspaceSymbol> symbols = indexer.getSymbols(docUri);
|
||||
assertEquals(1, symbols.size());
|
||||
assertTrue(containsSymbol(symbols, "@/request/mapping/path/from/same/class/constant", docUri, 8, 1, 8, 52));
|
||||
List<? extends WorkspaceSymbol> symbols = getSymbols(docUri);
|
||||
assertEquals(2, symbols.size());
|
||||
assertTrue(containsSymbol(symbols, "@/request/mapping/path/from/same/class/constant", docUri, 10, 1, 10, 52));
|
||||
|
||||
SpringIndexerJavaDependencyTracker dt = indexer.getJavaIndexer().getDependencyTracker();
|
||||
assertEquals(ImmutableSet.of(), dt.getAllDependencies().get(UriUtil.toFileString(docUri)));
|
||||
@@ -201,9 +205,9 @@ public class RequestMappingSymbolProviderTest {
|
||||
@Test
|
||||
void testSimpleRequestMappingSymbolFromConstantInBinaryType() throws Exception {
|
||||
String docUri = directory.toPath().resolve("src/main/java/org/test/SimpleMappingClassWithConstantFromBinaryType.java").toUri().toString();
|
||||
List<? extends WorkspaceSymbol> symbols = indexer.getSymbols(docUri);
|
||||
assertEquals(1, symbols.size());
|
||||
assertTrue(containsSymbol(symbols, "@/(inferred)", docUri, 7, 1, 7, 53));
|
||||
List<? extends WorkspaceSymbol> symbols = getSymbols(docUri);
|
||||
assertEquals(2, symbols.size());
|
||||
assertTrue(containsSymbol(symbols, "@/(inferred)", docUri, 9, 1, 9, 53));
|
||||
|
||||
SpringIndexerJavaDependencyTracker dt = indexer.getJavaIndexer().getDependencyTracker();
|
||||
assertEquals(ImmutableSet.of(), dt.getAllDependencies().get(UriUtil.toFileString(docUri)));
|
||||
@@ -212,79 +216,79 @@ public class RequestMappingSymbolProviderTest {
|
||||
@Test
|
||||
void testParentRequestMappingSymbol() throws Exception {
|
||||
String docUri = directory.toPath().resolve("src/main/java/org/test/ParentMappingClass.java").toUri().toString();
|
||||
List<? extends WorkspaceSymbol> symbols = indexer.getSymbols(docUri);
|
||||
assertEquals(1, symbols.size());
|
||||
assertTrue(containsSymbol(symbols, "@/parent/greeting -- GET", docUri, 8, 1, 8, 51));
|
||||
List<? extends WorkspaceSymbol> symbols = getSymbols(docUri);
|
||||
assertEquals(2, symbols.size());
|
||||
assertTrue(containsSymbol(symbols, "@/parent/greeting -- GET", docUri, 11, 1, 11, 51));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testEmptyPathWithParentRequestMappingSymbol() throws Exception {
|
||||
String docUri = directory.toPath().resolve("src/main/java/org/test/ParentMappingClass2.java").toUri().toString();
|
||||
List<? extends WorkspaceSymbol> symbols = indexer.getSymbols(docUri);
|
||||
assertEquals(1, symbols.size());
|
||||
assertTrue(containsSymbol(symbols, "@/parent2 -- GET,POST,DELETE", docUri, 11, 1, 11, 16));
|
||||
List<? extends WorkspaceSymbol> symbols = getSymbols(docUri);
|
||||
assertEquals(2, symbols.size());
|
||||
assertTrue(containsSymbol(symbols, "@/parent2 -- GET,POST,DELETE", docUri, 13, 1, 13, 16));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testParentRequestMappingSymbolWithPathAttribute() throws Exception {
|
||||
String docUri = directory.toPath().resolve("src/main/java/org/test/ParentMappingClass3.java").toUri().toString();
|
||||
List<? extends WorkspaceSymbol> symbols = indexer.getSymbols(docUri);
|
||||
assertEquals(1, symbols.size());
|
||||
assertTrue(containsSymbol(symbols, "@/parent3/greeting -- GET", docUri, 8, 1, 8, 51));
|
||||
List<? extends WorkspaceSymbol> symbols = getSymbols(docUri);
|
||||
assertEquals(2, symbols.size());
|
||||
assertTrue(containsSymbol(symbols, "@/parent3/greeting -- GET", docUri, 11, 1, 11, 51));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMappingPathFromSuperclass() throws Exception {
|
||||
String docUri = directory.toPath().resolve("src/main/java/org/test/inheritance/SubclassWithMappingFromParent.java").toUri().toString();
|
||||
List<? extends WorkspaceSymbol> symbols = indexer.getSymbols(docUri);
|
||||
assertEquals(1, symbols.size());
|
||||
assertTrue(containsSymbol(symbols, "@/superclasspath/", docUri, 6, 1, 6, 21));
|
||||
List<? extends WorkspaceSymbol> symbols = getSymbols(docUri);
|
||||
assertEquals(2, symbols.size());
|
||||
assertTrue(containsSymbol(symbols, "@/superclasspath/", docUri, 8, 1, 8, 21));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMappingPathFromSuperclassWithConstant() throws Exception {
|
||||
String docUri = directory.toPath().resolve("src/main/java/org/test/inheritance/SubclassWithMappingFromParentWithConstant.java").toUri().toString();
|
||||
List<? extends WorkspaceSymbol> symbols = indexer.getSymbols(docUri);
|
||||
assertEquals(1, symbols.size());
|
||||
assertTrue(containsSymbol(symbols, "@/path/from/constant/", docUri, 6, 1, 6, 21));
|
||||
List<? extends WorkspaceSymbol> symbols = getSymbols(docUri);
|
||||
assertEquals(2, symbols.size());
|
||||
assertTrue(containsSymbol(symbols, "@/path/from/constant/", docUri, 8, 1, 8, 21));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMappingPathFromSuperclassWithStringConcatenation() throws Exception {
|
||||
String docUri = directory.toPath().resolve("src/main/java/org/test/inheritance/SubclassWithMappingFromParentWithStringConcatenation.java").toUri().toString();
|
||||
List<? extends WorkspaceSymbol> symbols = indexer.getSymbols(docUri);
|
||||
assertEquals(1, symbols.size());
|
||||
assertTrue(containsSymbol(symbols, "@/superpath/subclass", docUri, 6, 1, 6, 34));
|
||||
List<? extends WorkspaceSymbol> symbols = getSymbols(docUri);
|
||||
assertEquals(2, symbols.size());
|
||||
assertTrue(containsSymbol(symbols, "@/superpath/subclass", docUri, 8, 1, 8, 34));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMappingPathFromSuperclassWithStringConcatenationPerAttribute() throws Exception {
|
||||
String docUri = directory.toPath().resolve("src/main/java/org/test/inheritance/SubclassWithMappingFromParentWithStringConcatenationPerAttribute.java").toUri().toString();
|
||||
List<? extends WorkspaceSymbol> symbols = indexer.getSymbols(docUri);
|
||||
assertEquals(1, symbols.size());
|
||||
assertTrue(containsSymbol(symbols, "@/superpath/subclass", docUri, 6, 1, 6, 42));
|
||||
List<? extends WorkspaceSymbol> symbols = getSymbols(docUri);
|
||||
assertEquals(2, symbols.size());
|
||||
assertTrue(containsSymbol(symbols, "@/superpath/subclass", docUri, 8, 1, 8, 42));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMappingPathFromSuperclassWithMethodsAndPathAttribute() throws Exception {
|
||||
String docUri = directory.toPath().resolve("src/main/java/org/test/inheritance/SubclassWithMappingFromParentWithMethods.java").toUri().toString();
|
||||
List<? extends WorkspaceSymbol> symbols = indexer.getSymbols(docUri);
|
||||
assertEquals(1, symbols.size());
|
||||
assertTrue(containsSymbol(symbols, "@/superclasspath -- POST,PUT - Accept: testconsume - Content-Type: text/plain", docUri, 6, 1, 6, 16));
|
||||
List<? extends WorkspaceSymbol> symbols = getSymbols(docUri);
|
||||
assertEquals(2, symbols.size());
|
||||
assertTrue(containsSymbol(symbols, "@/superclasspath -- POST,PUT - Accept: testconsume - Content-Type: text/plain", docUri, 8, 1, 8, 16));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMappingPathFromMultiLevelClassHierarchy() throws Exception {
|
||||
String docUri = directory.toPath().resolve("src/main/java/org/test/inheritance/SuperControllerLevel4.java").toUri().toString();
|
||||
List<? extends WorkspaceSymbol> symbols = indexer.getSymbols(docUri);
|
||||
assertEquals(1, symbols.size());
|
||||
assertTrue(containsSymbol(symbols, "@/path-level2/final-subclass-path", docUri, 6, 1, 6, 39));
|
||||
List<? extends WorkspaceSymbol> symbols = getSymbols(docUri);
|
||||
assertEquals(2, symbols.size());
|
||||
assertTrue(containsSymbol(symbols, "@/path-level2/final-subclass-path", docUri, 8, 1, 8, 39));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMappingPathFromSuperInterfaceEvenIfSuperclassContainsMappingPath() throws Exception {
|
||||
String docUri = directory.toPath().resolve("src/main/java/org/test/inheritance/ControllerAsSubclassAndInterfaceHierarchy.java").toUri().toString();
|
||||
List<? extends WorkspaceSymbol> symbols = indexer.getSymbols(docUri);
|
||||
List<? extends WorkspaceSymbol> symbols = getSymbols(docUri);
|
||||
assertEquals(2, symbols.size());
|
||||
assertTrue(containsSymbol(symbols, "@/superinterface-path/last-path-segment -- GET - Accept: testconsume - Content-Type: text/plain", docUri, 8, 1, 8, 33));
|
||||
}
|
||||
@@ -317,101 +321,101 @@ public class RequestMappingSymbolProviderTest {
|
||||
@Test
|
||||
void testMultiRequestMappingSymbol() throws Exception {
|
||||
String docUri = directory.toPath().resolve("src/main/java/org/test/MultiRequestMappingClass.java").toUri().toString();
|
||||
List<? extends WorkspaceSymbol> symbols = indexer.getSymbols(docUri);
|
||||
assertEquals(2, symbols.size());
|
||||
assertTrue(containsSymbol(symbols, "@/hello1", docUri, 6, 1, 6, 44));
|
||||
assertTrue(containsSymbol(symbols, "@/hello2", docUri, 6, 1, 6, 44));
|
||||
List<? extends WorkspaceSymbol> symbols = getSymbols(docUri);
|
||||
assertEquals(3, symbols.size());
|
||||
assertTrue(containsSymbol(symbols, "@/hello1", docUri, 8, 1, 8, 44));
|
||||
assertTrue(containsSymbol(symbols, "@/hello2", docUri, 8, 1, 8, 44));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetMappingSymbol() throws Exception {
|
||||
String docUri = directory.toPath().resolve("src/main/java/org/test/RequestMethodClass.java").toUri().toString();
|
||||
List<? extends WorkspaceSymbol> symbols = indexer.getSymbols(docUri);
|
||||
assertTrue(containsSymbol(symbols, "@/getData -- GET", docUri, 13, 1, 13, 24));
|
||||
List<? extends WorkspaceSymbol> symbols = getSymbols(docUri);
|
||||
assertTrue(containsSymbol(symbols, "@/getData -- GET", docUri, 16, 1, 16, 24));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetMappingSymbolWithoutPath() throws Exception {
|
||||
String docUri = directory.toPath().resolve("src/main/java/org/test/RequestMethodClass.java").toUri().toString();
|
||||
List<? extends WorkspaceSymbol> symbols = indexer.getSymbols(docUri);
|
||||
assertTrue(containsSymbol(symbols, "@/ -- GET", docUri, 41, 1, 41, 16));
|
||||
List<? extends WorkspaceSymbol> symbols = getSymbols(docUri);
|
||||
assertTrue(containsSymbol(symbols, "@/ -- GET", docUri, 44, 1, 44, 16));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetMappingSymbolWithoutAnything() throws Exception {
|
||||
String docUri = directory.toPath().resolve("src/main/java/org/test/RequestMethodClass.java").toUri().toString();
|
||||
List<? extends WorkspaceSymbol> symbols = indexer.getSymbols(docUri);
|
||||
assertTrue(containsSymbol(symbols, "@/ -- GET", docUri, 45, 1, 45, 14));
|
||||
List<? extends WorkspaceSymbol> symbols = getSymbols(docUri);
|
||||
assertTrue(containsSymbol(symbols, "@/ -- GET", docUri, 48, 1, 48, 14));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDeleteMappingSymbol() throws Exception {
|
||||
String docUri = directory.toPath().resolve("src/main/java/org/test/RequestMethodClass.java").toUri().toString();
|
||||
List<? extends WorkspaceSymbol> symbols = indexer.getSymbols(docUri);
|
||||
assertTrue(containsSymbol(symbols, "@/deleteData -- DELETE", docUri, 21, 1, 21, 30));
|
||||
List<? extends WorkspaceSymbol> symbols = getSymbols(docUri);
|
||||
assertTrue(containsSymbol(symbols, "@/deleteData -- DELETE", docUri, 24, 1, 24, 30));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPostMappingSymbol() throws Exception {
|
||||
String docUri = directory.toPath().resolve("src/main/java/org/test/RequestMethodClass.java").toUri().toString();
|
||||
List<? extends WorkspaceSymbol> symbols = indexer.getSymbols(docUri);
|
||||
assertTrue(containsSymbol(symbols, "@/postData -- POST", docUri, 25, 1, 25, 26));
|
||||
List<? extends WorkspaceSymbol> symbols = getSymbols(docUri);
|
||||
assertTrue(containsSymbol(symbols, "@/postData -- POST", docUri, 28, 1, 28, 26));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPutMappingSymbol() throws Exception {
|
||||
String docUri = directory.toPath().resolve("src/main/java/org/test/RequestMethodClass.java").toUri().toString();
|
||||
List<? extends WorkspaceSymbol> symbols = indexer.getSymbols(docUri);
|
||||
assertTrue(containsSymbol(symbols, "@/putData -- PUT", docUri, 17, 1, 17, 24));
|
||||
List<? extends WorkspaceSymbol> symbols = getSymbols(docUri);
|
||||
assertTrue(containsSymbol(symbols, "@/putData -- PUT", docUri, 20, 1, 20, 24));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPatchMappingSymbol() throws Exception {
|
||||
String docUri = directory.toPath().resolve("src/main/java/org/test/RequestMethodClass.java").toUri().toString();
|
||||
List<? extends WorkspaceSymbol> symbols = indexer.getSymbols(docUri);
|
||||
assertTrue(containsSymbol(symbols, "@/patchData -- PATCH", docUri, 29, 1, 29, 28));
|
||||
List<? extends WorkspaceSymbol> symbols = getSymbols(docUri);
|
||||
assertTrue(containsSymbol(symbols, "@/patchData -- PATCH", docUri, 32, 1, 32, 28));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetRequestMappingSymbol() throws Exception {
|
||||
String docUri = directory.toPath().resolve("src/main/java/org/test/RequestMethodClass.java").toUri().toString();
|
||||
List<? extends WorkspaceSymbol> symbols = indexer.getSymbols(docUri);
|
||||
assertTrue(containsSymbol(symbols, "@/getHello -- GET", docUri, 33, 1, 33, 61));
|
||||
List<? extends WorkspaceSymbol> symbols = getSymbols(docUri);
|
||||
assertTrue(containsSymbol(symbols, "@/getHello -- GET", docUri, 36, 1, 36, 61));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMultiRequestMethodMappingSymbol() throws Exception {
|
||||
String docUri = directory.toPath().resolve("src/main/java/org/test/RequestMethodClass.java").toUri().toString();
|
||||
List<? extends WorkspaceSymbol> symbols = indexer.getSymbols(docUri);
|
||||
assertTrue(containsSymbol(symbols, "@/postAndPutHello -- POST,PUT", docUri, 37, 1, 37, 76));
|
||||
List<? extends WorkspaceSymbol> symbols = getSymbols(docUri);
|
||||
assertTrue(containsSymbol(symbols, "@/postAndPutHello -- POST,PUT", docUri, 40, 1, 40, 76));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMediaTypes() throws Exception {
|
||||
String docUri = directory.toPath().resolve("src/main/java/org/test/RequestMappingMediaTypes.java").toUri().toString();
|
||||
List<? extends WorkspaceSymbol> symbols = indexer.getSymbols(docUri);
|
||||
assertEquals(7, symbols.size());
|
||||
assertTrue(containsSymbol(symbols, "@/consume1 -- HEAD - Accept: testconsume", docUri, 8, 1, 8, 90));
|
||||
assertTrue(containsSymbol(symbols, "@/consume2 - Accept: text/plain", docUri, 13, 1, 13, 73));
|
||||
assertTrue(containsSymbol(symbols, "@/consume3 - Accept: text/plain,testconsumetype", docUri, 18, 1, 18, 94));
|
||||
assertTrue(containsSymbol(symbols, "@/produce1 - Content-Type: testproduce", docUri, 23, 1, 23, 60));
|
||||
assertTrue(containsSymbol(symbols, "@/produce2 - Content-Type: text/plain", docUri, 28, 1, 28, 73));
|
||||
assertTrue(containsSymbol(symbols, "@/produce3 - Content-Type: text/plain,testproducetype", docUri, 33, 1, 33, 94));
|
||||
assertTrue(containsSymbol(symbols, "@/everything - Accept: application/json,text/plain,testconsume - Content-Type: application/json", docUri, 38, 1, 38, 170));
|
||||
List<? extends WorkspaceSymbol> symbols = getSymbols(docUri);
|
||||
assertEquals(8, symbols.size());
|
||||
assertTrue(containsSymbol(symbols, "@/consume1 -- HEAD - Accept: testconsume", docUri, 10, 1, 10, 90));
|
||||
assertTrue(containsSymbol(symbols, "@/consume2 - Accept: text/plain", docUri, 15, 1, 15, 73));
|
||||
assertTrue(containsSymbol(symbols, "@/consume3 - Accept: text/plain,testconsumetype", docUri, 20, 1, 20, 94));
|
||||
assertTrue(containsSymbol(symbols, "@/produce1 - Content-Type: testproduce", docUri, 25, 1, 25, 60));
|
||||
assertTrue(containsSymbol(symbols, "@/produce2 - Content-Type: text/plain", docUri, 30, 1, 30, 73));
|
||||
assertTrue(containsSymbol(symbols, "@/produce3 - Content-Type: text/plain,testproducetype", docUri, 35, 1, 35, 94));
|
||||
assertTrue(containsSymbol(symbols, "@/everything - Accept: application/json,text/plain,testconsume - Content-Type: application/json", docUri, 40, 1, 40, 170));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPathWithConcatenatedString() throws Exception {
|
||||
String docUri = directory.toPath().resolve("src/main/java/org/test/MappingsWithConcatenatedStrings.java").toUri().toString();
|
||||
List<? extends WorkspaceSymbol> symbols = indexer.getSymbols(docUri);
|
||||
assertTrue(containsSymbol(symbols, "@/path1/path2 -- GET", docUri, 13, 1, 13, 33));
|
||||
List<? extends WorkspaceSymbol> symbols = getSymbols(docUri);
|
||||
assertTrue(containsSymbol(symbols, "@/path1/path2 -- GET", docUri, 16, 1, 16, 33));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPathWithConcatenatedStringAndConstantInvolved() throws Exception {
|
||||
String docUri = directory.toPath().resolve("src/main/java/org/test/MappingsWithConcatenatedStrings.java").toUri().toString();
|
||||
List<? extends WorkspaceSymbol> symbols = indexer.getSymbols(docUri);
|
||||
assertTrue(containsSymbol(symbols, "@/path1/path/from/constant -- GET", docUri, 17, 1, 17, 56));
|
||||
List<? extends WorkspaceSymbol> symbols = getSymbols(docUri);
|
||||
assertTrue(containsSymbol(symbols, "@/path1/path/from/constant -- GET", docUri, 20, 1, 20, 56));
|
||||
}
|
||||
|
||||
private boolean containsSymbol(List<? extends WorkspaceSymbol> symbols, String name, String uri, int startLine, int startCHaracter, int endLine, int endCharacter) {
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.example.events.demo;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Qualifier("qualifier")
|
||||
@Component
|
||||
public class CustomEventPublisherWithAdditionalElements {
|
||||
|
||||
private ApplicationEventPublisher publisher;
|
||||
|
||||
public CustomEventPublisherWithAdditionalElements(ApplicationEventPublisher publisher) {
|
||||
this.publisher = publisher;
|
||||
}
|
||||
|
||||
public void foo() {
|
||||
this.publisher.publishEvent(new CustomEvent());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
package org.test;
|
||||
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
|
||||
@Controller
|
||||
public class ChainedRequestMappingPathOverMultipleClasses {
|
||||
|
||||
@RequestMapping(ChainElement1.MAPPING_PATH_1)
|
||||
|
||||
@@ -9,6 +9,9 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import static org.springframework.web.bind.annotation.RequestMethod.PUT;
|
||||
|
||||
import org.springframework.stereotype.Controller;
|
||||
|
||||
@Controller
|
||||
public class MappingsWithConcatenatedStrings {
|
||||
|
||||
@GetMapping("/path1" + "/path2")
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package org.test;
|
||||
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
|
||||
@Controller
|
||||
public class MultiRequestMappingClass {
|
||||
|
||||
@RequestMapping(path= {"/hello1","hello2"})
|
||||
|
||||
@@ -3,6 +3,9 @@ package org.test;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import static org.springframework.web.bind.annotation.RequestMethod.*;
|
||||
|
||||
import org.springframework.stereotype.Controller;
|
||||
|
||||
@Controller
|
||||
@RequestMapping(value = "parent", method = {GET,POST,DELETE})
|
||||
public class ParentMappingClass {
|
||||
|
||||
|
||||
@@ -4,8 +4,10 @@ import static org.springframework.web.bind.annotation.RequestMethod.DELETE;
|
||||
import static org.springframework.web.bind.annotation.RequestMethod.GET;
|
||||
import static org.springframework.web.bind.annotation.RequestMethod.POST;
|
||||
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
|
||||
@Controller
|
||||
@RequestMapping(value = "parent2", method = {GET,POST,DELETE})
|
||||
public class ParentMappingClass2 {
|
||||
|
||||
|
||||
@@ -3,6 +3,9 @@ package org.test;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import static org.springframework.web.bind.annotation.RequestMethod.*;
|
||||
|
||||
import org.springframework.stereotype.Controller;
|
||||
|
||||
@Controller
|
||||
@RequestMapping(path = "parent3", method = {GET,POST,DELETE})
|
||||
public class ParentMappingClass3 {
|
||||
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package org.test;
|
||||
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
public class PingConstantRequestMapping {
|
||||
|
||||
public final static String PING = "/ping";
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package org.test;
|
||||
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
public class PongConstantRequestMapping {
|
||||
|
||||
public static final String PONG="/pong";
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
package org.test;
|
||||
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
|
||||
@Controller
|
||||
public class RequestMappingMediaTypes {
|
||||
|
||||
@RequestMapping(path="/consume1", consumes = "testconsume", method= {RequestMethod.HEAD})
|
||||
|
||||
@@ -9,6 +9,9 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import static org.springframework.web.bind.annotation.RequestMethod.PUT;
|
||||
|
||||
import org.springframework.stereotype.Controller;
|
||||
|
||||
@Controller
|
||||
public class RequestMethodClass {
|
||||
|
||||
@GetMapping("/getData")
|
||||
|
||||
@@ -2,7 +2,9 @@ package org.test;
|
||||
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.beans.factory.support.AbstractBeanDefinition;
|
||||
import org.springframework.stereotype.Controller;
|
||||
|
||||
@Controller
|
||||
public class SimpleMappingClassWithConstantFromBinaryType {
|
||||
|
||||
@RequestMapping(AbstractBeanDefinition.INFER_METHOD)
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package org.test;
|
||||
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
|
||||
@Controller
|
||||
public class SimpleMappingClassWithConstantInDifferentClass {
|
||||
|
||||
@RequestMapping(Constants.REQUEST_MAPPING_PATH)
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package org.test;
|
||||
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
|
||||
@Controller
|
||||
public class SimpleMappingClassWithConstantInSameClass {
|
||||
|
||||
private static final String REQUEST_MAPPING_PATH_IN_SAME_CLASS = "/request/mapping/path/from/same/class/constant";
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package org.test.inheritance;
|
||||
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
|
||||
@Controller
|
||||
public class SubclassWithMappingFromParent extends SuperclassWithMappingPath {
|
||||
|
||||
@RequestMapping("/")
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package org.test.inheritance;
|
||||
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
|
||||
@Controller
|
||||
public class SubclassWithMappingFromParentWithConstant extends SuperclassWithMappingPathFromConstant {
|
||||
|
||||
@RequestMapping("/")
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package org.test.inheritance;
|
||||
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
|
||||
@Controller
|
||||
public class SubclassWithMappingFromParentWithMethods extends SuperclassWithMappingPathAndMethods {
|
||||
|
||||
@RequestMapping
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package org.test.inheritance;
|
||||
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
|
||||
@Controller
|
||||
public class SubclassWithMappingFromParentWithStringConcatenation extends SuperclassWithMappingPathWithStringConcatenation {
|
||||
|
||||
@RequestMapping("/sub" + "class")
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package org.test.inheritance;
|
||||
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
|
||||
@Controller
|
||||
public class SubclassWithMappingFromParentWithStringConcatenationPerAttribute extends SuperclassWithMappingPathWithStringConcatenationPerAttribute {
|
||||
|
||||
@RequestMapping(value = "/sub" + "class")
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package org.test.inheritance;
|
||||
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
|
||||
@Controller
|
||||
public class SuperControllerLevel4 extends SuperControllerLevel3 {
|
||||
|
||||
@RequestMapping("final-subclass-path")
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
wrapperVersion=3.3.2
|
||||
distributionType=only-script
|
||||
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip
|
||||
259
headless-services/spring-boot-language-server/src/test/resources/test-projects/test-spring-index-structure/mvnw
vendored
Executable file
259
headless-services/spring-boot-language-server/src/test/resources/test-projects/test-spring-index-structure/mvnw
vendored
Executable file
@@ -0,0 +1,259 @@
|
||||
#!/bin/sh
|
||||
# ----------------------------------------------------------------------------
|
||||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# Apache Maven Wrapper startup batch script, version 3.3.2
|
||||
#
|
||||
# Optional ENV vars
|
||||
# -----------------
|
||||
# JAVA_HOME - location of a JDK home dir, required when download maven via java source
|
||||
# MVNW_REPOURL - repo url base for downloading maven distribution
|
||||
# MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven
|
||||
# MVNW_VERBOSE - true: enable verbose log; debug: trace the mvnw script; others: silence the output
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
set -euf
|
||||
[ "${MVNW_VERBOSE-}" != debug ] || set -x
|
||||
|
||||
# OS specific support.
|
||||
native_path() { printf %s\\n "$1"; }
|
||||
case "$(uname)" in
|
||||
CYGWIN* | MINGW*)
|
||||
[ -z "${JAVA_HOME-}" ] || JAVA_HOME="$(cygpath --unix "$JAVA_HOME")"
|
||||
native_path() { cygpath --path --windows "$1"; }
|
||||
;;
|
||||
esac
|
||||
|
||||
# set JAVACMD and JAVACCMD
|
||||
set_java_home() {
|
||||
# For Cygwin and MinGW, ensure paths are in Unix format before anything is touched
|
||||
if [ -n "${JAVA_HOME-}" ]; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ]; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
JAVACCMD="$JAVA_HOME/jre/sh/javac"
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
JAVACCMD="$JAVA_HOME/bin/javac"
|
||||
|
||||
if [ ! -x "$JAVACMD" ] || [ ! -x "$JAVACCMD" ]; then
|
||||
echo "The JAVA_HOME environment variable is not defined correctly, so mvnw cannot run." >&2
|
||||
echo "JAVA_HOME is set to \"$JAVA_HOME\", but \"\$JAVA_HOME/bin/java\" or \"\$JAVA_HOME/bin/javac\" does not exist." >&2
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
else
|
||||
JAVACMD="$(
|
||||
'set' +e
|
||||
'unset' -f command 2>/dev/null
|
||||
'command' -v java
|
||||
)" || :
|
||||
JAVACCMD="$(
|
||||
'set' +e
|
||||
'unset' -f command 2>/dev/null
|
||||
'command' -v javac
|
||||
)" || :
|
||||
|
||||
if [ ! -x "${JAVACMD-}" ] || [ ! -x "${JAVACCMD-}" ]; then
|
||||
echo "The java/javac command does not exist in PATH nor is JAVA_HOME set, so mvnw cannot run." >&2
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# hash string like Java String::hashCode
|
||||
hash_string() {
|
||||
str="${1:-}" h=0
|
||||
while [ -n "$str" ]; do
|
||||
char="${str%"${str#?}"}"
|
||||
h=$(((h * 31 + $(LC_CTYPE=C printf %d "'$char")) % 4294967296))
|
||||
str="${str#?}"
|
||||
done
|
||||
printf %x\\n $h
|
||||
}
|
||||
|
||||
verbose() { :; }
|
||||
[ "${MVNW_VERBOSE-}" != true ] || verbose() { printf %s\\n "${1-}"; }
|
||||
|
||||
die() {
|
||||
printf %s\\n "$1" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
trim() {
|
||||
# MWRAPPER-139:
|
||||
# Trims trailing and leading whitespace, carriage returns, tabs, and linefeeds.
|
||||
# Needed for removing poorly interpreted newline sequences when running in more
|
||||
# exotic environments such as mingw bash on Windows.
|
||||
printf "%s" "${1}" | tr -d '[:space:]'
|
||||
}
|
||||
|
||||
# parse distributionUrl and optional distributionSha256Sum, requires .mvn/wrapper/maven-wrapper.properties
|
||||
while IFS="=" read -r key value; do
|
||||
case "${key-}" in
|
||||
distributionUrl) distributionUrl=$(trim "${value-}") ;;
|
||||
distributionSha256Sum) distributionSha256Sum=$(trim "${value-}") ;;
|
||||
esac
|
||||
done <"${0%/*}/.mvn/wrapper/maven-wrapper.properties"
|
||||
[ -n "${distributionUrl-}" ] || die "cannot read distributionUrl property in ${0%/*}/.mvn/wrapper/maven-wrapper.properties"
|
||||
|
||||
case "${distributionUrl##*/}" in
|
||||
maven-mvnd-*bin.*)
|
||||
MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/
|
||||
case "${PROCESSOR_ARCHITECTURE-}${PROCESSOR_ARCHITEW6432-}:$(uname -a)" in
|
||||
*AMD64:CYGWIN* | *AMD64:MINGW*) distributionPlatform=windows-amd64 ;;
|
||||
:Darwin*x86_64) distributionPlatform=darwin-amd64 ;;
|
||||
:Darwin*arm64) distributionPlatform=darwin-aarch64 ;;
|
||||
:Linux*x86_64*) distributionPlatform=linux-amd64 ;;
|
||||
*)
|
||||
echo "Cannot detect native platform for mvnd on $(uname)-$(uname -m), use pure java version" >&2
|
||||
distributionPlatform=linux-amd64
|
||||
;;
|
||||
esac
|
||||
distributionUrl="${distributionUrl%-bin.*}-$distributionPlatform.zip"
|
||||
;;
|
||||
maven-mvnd-*) MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ ;;
|
||||
*) MVN_CMD="mvn${0##*/mvnw}" _MVNW_REPO_PATTERN=/org/apache/maven/ ;;
|
||||
esac
|
||||
|
||||
# apply MVNW_REPOURL and calculate MAVEN_HOME
|
||||
# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-<version>,maven-mvnd-<version>-<platform>}/<hash>
|
||||
[ -z "${MVNW_REPOURL-}" ] || distributionUrl="$MVNW_REPOURL$_MVNW_REPO_PATTERN${distributionUrl#*"$_MVNW_REPO_PATTERN"}"
|
||||
distributionUrlName="${distributionUrl##*/}"
|
||||
distributionUrlNameMain="${distributionUrlName%.*}"
|
||||
distributionUrlNameMain="${distributionUrlNameMain%-bin}"
|
||||
MAVEN_USER_HOME="${MAVEN_USER_HOME:-${HOME}/.m2}"
|
||||
MAVEN_HOME="${MAVEN_USER_HOME}/wrapper/dists/${distributionUrlNameMain-}/$(hash_string "$distributionUrl")"
|
||||
|
||||
exec_maven() {
|
||||
unset MVNW_VERBOSE MVNW_USERNAME MVNW_PASSWORD MVNW_REPOURL || :
|
||||
exec "$MAVEN_HOME/bin/$MVN_CMD" "$@" || die "cannot exec $MAVEN_HOME/bin/$MVN_CMD"
|
||||
}
|
||||
|
||||
if [ -d "$MAVEN_HOME" ]; then
|
||||
verbose "found existing MAVEN_HOME at $MAVEN_HOME"
|
||||
exec_maven "$@"
|
||||
fi
|
||||
|
||||
case "${distributionUrl-}" in
|
||||
*?-bin.zip | *?maven-mvnd-?*-?*.zip) ;;
|
||||
*) die "distributionUrl is not valid, must match *-bin.zip or maven-mvnd-*.zip, but found '${distributionUrl-}'" ;;
|
||||
esac
|
||||
|
||||
# prepare tmp dir
|
||||
if TMP_DOWNLOAD_DIR="$(mktemp -d)" && [ -d "$TMP_DOWNLOAD_DIR" ]; then
|
||||
clean() { rm -rf -- "$TMP_DOWNLOAD_DIR"; }
|
||||
trap clean HUP INT TERM EXIT
|
||||
else
|
||||
die "cannot create temp dir"
|
||||
fi
|
||||
|
||||
mkdir -p -- "${MAVEN_HOME%/*}"
|
||||
|
||||
# Download and Install Apache Maven
|
||||
verbose "Couldn't find MAVEN_HOME, downloading and installing it ..."
|
||||
verbose "Downloading from: $distributionUrl"
|
||||
verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName"
|
||||
|
||||
# select .zip or .tar.gz
|
||||
if ! command -v unzip >/dev/null; then
|
||||
distributionUrl="${distributionUrl%.zip}.tar.gz"
|
||||
distributionUrlName="${distributionUrl##*/}"
|
||||
fi
|
||||
|
||||
# verbose opt
|
||||
__MVNW_QUIET_WGET=--quiet __MVNW_QUIET_CURL=--silent __MVNW_QUIET_UNZIP=-q __MVNW_QUIET_TAR=''
|
||||
[ "${MVNW_VERBOSE-}" != true ] || __MVNW_QUIET_WGET='' __MVNW_QUIET_CURL='' __MVNW_QUIET_UNZIP='' __MVNW_QUIET_TAR=v
|
||||
|
||||
# normalize http auth
|
||||
case "${MVNW_PASSWORD:+has-password}" in
|
||||
'') MVNW_USERNAME='' MVNW_PASSWORD='' ;;
|
||||
has-password) [ -n "${MVNW_USERNAME-}" ] || MVNW_USERNAME='' MVNW_PASSWORD='' ;;
|
||||
esac
|
||||
|
||||
if [ -z "${MVNW_USERNAME-}" ] && command -v wget >/dev/null; then
|
||||
verbose "Found wget ... using wget"
|
||||
wget ${__MVNW_QUIET_WGET:+"$__MVNW_QUIET_WGET"} "$distributionUrl" -O "$TMP_DOWNLOAD_DIR/$distributionUrlName" || die "wget: Failed to fetch $distributionUrl"
|
||||
elif [ -z "${MVNW_USERNAME-}" ] && command -v curl >/dev/null; then
|
||||
verbose "Found curl ... using curl"
|
||||
curl ${__MVNW_QUIET_CURL:+"$__MVNW_QUIET_CURL"} -f -L -o "$TMP_DOWNLOAD_DIR/$distributionUrlName" "$distributionUrl" || die "curl: Failed to fetch $distributionUrl"
|
||||
elif set_java_home; then
|
||||
verbose "Falling back to use Java to download"
|
||||
javaSource="$TMP_DOWNLOAD_DIR/Downloader.java"
|
||||
targetZip="$TMP_DOWNLOAD_DIR/$distributionUrlName"
|
||||
cat >"$javaSource" <<-END
|
||||
public class Downloader extends java.net.Authenticator
|
||||
{
|
||||
protected java.net.PasswordAuthentication getPasswordAuthentication()
|
||||
{
|
||||
return new java.net.PasswordAuthentication( System.getenv( "MVNW_USERNAME" ), System.getenv( "MVNW_PASSWORD" ).toCharArray() );
|
||||
}
|
||||
public static void main( String[] args ) throws Exception
|
||||
{
|
||||
setDefault( new Downloader() );
|
||||
java.nio.file.Files.copy( java.net.URI.create( args[0] ).toURL().openStream(), java.nio.file.Paths.get( args[1] ).toAbsolutePath().normalize() );
|
||||
}
|
||||
}
|
||||
END
|
||||
# For Cygwin/MinGW, switch paths to Windows format before running javac and java
|
||||
verbose " - Compiling Downloader.java ..."
|
||||
"$(native_path "$JAVACCMD")" "$(native_path "$javaSource")" || die "Failed to compile Downloader.java"
|
||||
verbose " - Running Downloader.java ..."
|
||||
"$(native_path "$JAVACMD")" -cp "$(native_path "$TMP_DOWNLOAD_DIR")" Downloader "$distributionUrl" "$(native_path "$targetZip")"
|
||||
fi
|
||||
|
||||
# If specified, validate the SHA-256 sum of the Maven distribution zip file
|
||||
if [ -n "${distributionSha256Sum-}" ]; then
|
||||
distributionSha256Result=false
|
||||
if [ "$MVN_CMD" = mvnd.sh ]; then
|
||||
echo "Checksum validation is not supported for maven-mvnd." >&2
|
||||
echo "Please disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2
|
||||
exit 1
|
||||
elif command -v sha256sum >/dev/null; then
|
||||
if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | sha256sum -c >/dev/null 2>&1; then
|
||||
distributionSha256Result=true
|
||||
fi
|
||||
elif command -v shasum >/dev/null; then
|
||||
if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | shasum -a 256 -c >/dev/null 2>&1; then
|
||||
distributionSha256Result=true
|
||||
fi
|
||||
else
|
||||
echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." >&2
|
||||
echo "Please install either command, or disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2
|
||||
exit 1
|
||||
fi
|
||||
if [ $distributionSha256Result = false ]; then
|
||||
echo "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised." >&2
|
||||
echo "If you updated your Maven version, you need to update the specified distributionSha256Sum property." >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# unzip and move
|
||||
if command -v unzip >/dev/null; then
|
||||
unzip ${__MVNW_QUIET_UNZIP:+"$__MVNW_QUIET_UNZIP"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -d "$TMP_DOWNLOAD_DIR" || die "failed to unzip"
|
||||
else
|
||||
tar xzf${__MVNW_QUIET_TAR:+"$__MVNW_QUIET_TAR"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -C "$TMP_DOWNLOAD_DIR" || die "failed to untar"
|
||||
fi
|
||||
printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/mvnw.url"
|
||||
mv -- "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME"
|
||||
|
||||
clean || :
|
||||
exec_maven "$@"
|
||||
@@ -0,0 +1,149 @@
|
||||
<# : batch portion
|
||||
@REM ----------------------------------------------------------------------------
|
||||
@REM Licensed to the Apache Software Foundation (ASF) under one
|
||||
@REM or more contributor license agreements. See the NOTICE file
|
||||
@REM distributed with this work for additional information
|
||||
@REM regarding copyright ownership. The ASF licenses this file
|
||||
@REM to you under the Apache License, Version 2.0 (the
|
||||
@REM "License"); you may not use this file except in compliance
|
||||
@REM with the License. You may obtain a copy of the License at
|
||||
@REM
|
||||
@REM https://www.apache.org/licenses/LICENSE-2.0
|
||||
@REM
|
||||
@REM Unless required by applicable law or agreed to in writing,
|
||||
@REM software distributed under the License is distributed on an
|
||||
@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
@REM KIND, either express or implied. See the License for the
|
||||
@REM specific language governing permissions and limitations
|
||||
@REM under the License.
|
||||
@REM ----------------------------------------------------------------------------
|
||||
|
||||
@REM ----------------------------------------------------------------------------
|
||||
@REM Apache Maven Wrapper startup batch script, version 3.3.2
|
||||
@REM
|
||||
@REM Optional ENV vars
|
||||
@REM MVNW_REPOURL - repo url base for downloading maven distribution
|
||||
@REM MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven
|
||||
@REM MVNW_VERBOSE - true: enable verbose log; others: silence the output
|
||||
@REM ----------------------------------------------------------------------------
|
||||
|
||||
@IF "%__MVNW_ARG0_NAME__%"=="" (SET __MVNW_ARG0_NAME__=%~nx0)
|
||||
@SET __MVNW_CMD__=
|
||||
@SET __MVNW_ERROR__=
|
||||
@SET __MVNW_PSMODULEP_SAVE=%PSModulePath%
|
||||
@SET PSModulePath=
|
||||
@FOR /F "usebackq tokens=1* delims==" %%A IN (`powershell -noprofile "& {$scriptDir='%~dp0'; $script='%__MVNW_ARG0_NAME__%'; icm -ScriptBlock ([Scriptblock]::Create((Get-Content -Raw '%~f0'))) -NoNewScope}"`) DO @(
|
||||
IF "%%A"=="MVN_CMD" (set __MVNW_CMD__=%%B) ELSE IF "%%B"=="" (echo %%A) ELSE (echo %%A=%%B)
|
||||
)
|
||||
@SET PSModulePath=%__MVNW_PSMODULEP_SAVE%
|
||||
@SET __MVNW_PSMODULEP_SAVE=
|
||||
@SET __MVNW_ARG0_NAME__=
|
||||
@SET MVNW_USERNAME=
|
||||
@SET MVNW_PASSWORD=
|
||||
@IF NOT "%__MVNW_CMD__%"=="" (%__MVNW_CMD__% %*)
|
||||
@echo Cannot start maven from wrapper >&2 && exit /b 1
|
||||
@GOTO :EOF
|
||||
: end batch / begin powershell #>
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
if ($env:MVNW_VERBOSE -eq "true") {
|
||||
$VerbosePreference = "Continue"
|
||||
}
|
||||
|
||||
# calculate distributionUrl, requires .mvn/wrapper/maven-wrapper.properties
|
||||
$distributionUrl = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionUrl
|
||||
if (!$distributionUrl) {
|
||||
Write-Error "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties"
|
||||
}
|
||||
|
||||
switch -wildcard -casesensitive ( $($distributionUrl -replace '^.*/','') ) {
|
||||
"maven-mvnd-*" {
|
||||
$USE_MVND = $true
|
||||
$distributionUrl = $distributionUrl -replace '-bin\.[^.]*$',"-windows-amd64.zip"
|
||||
$MVN_CMD = "mvnd.cmd"
|
||||
break
|
||||
}
|
||||
default {
|
||||
$USE_MVND = $false
|
||||
$MVN_CMD = $script -replace '^mvnw','mvn'
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
# apply MVNW_REPOURL and calculate MAVEN_HOME
|
||||
# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-<version>,maven-mvnd-<version>-<platform>}/<hash>
|
||||
if ($env:MVNW_REPOURL) {
|
||||
$MVNW_REPO_PATTERN = if ($USE_MVND) { "/org/apache/maven/" } else { "/maven/mvnd/" }
|
||||
$distributionUrl = "$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace '^.*'+$MVNW_REPO_PATTERN,'')"
|
||||
}
|
||||
$distributionUrlName = $distributionUrl -replace '^.*/',''
|
||||
$distributionUrlNameMain = $distributionUrlName -replace '\.[^.]*$','' -replace '-bin$',''
|
||||
$MAVEN_HOME_PARENT = "$HOME/.m2/wrapper/dists/$distributionUrlNameMain"
|
||||
if ($env:MAVEN_USER_HOME) {
|
||||
$MAVEN_HOME_PARENT = "$env:MAVEN_USER_HOME/wrapper/dists/$distributionUrlNameMain"
|
||||
}
|
||||
$MAVEN_HOME_NAME = ([System.Security.Cryptography.MD5]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join ''
|
||||
$MAVEN_HOME = "$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME"
|
||||
|
||||
if (Test-Path -Path "$MAVEN_HOME" -PathType Container) {
|
||||
Write-Verbose "found existing MAVEN_HOME at $MAVEN_HOME"
|
||||
Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD"
|
||||
exit $?
|
||||
}
|
||||
|
||||
if (! $distributionUrlNameMain -or ($distributionUrlName -eq $distributionUrlNameMain)) {
|
||||
Write-Error "distributionUrl is not valid, must end with *-bin.zip, but found $distributionUrl"
|
||||
}
|
||||
|
||||
# prepare tmp dir
|
||||
$TMP_DOWNLOAD_DIR_HOLDER = New-TemporaryFile
|
||||
$TMP_DOWNLOAD_DIR = New-Item -Itemtype Directory -Path "$TMP_DOWNLOAD_DIR_HOLDER.dir"
|
||||
$TMP_DOWNLOAD_DIR_HOLDER.Delete() | Out-Null
|
||||
trap {
|
||||
if ($TMP_DOWNLOAD_DIR.Exists) {
|
||||
try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null }
|
||||
catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" }
|
||||
}
|
||||
}
|
||||
|
||||
New-Item -Itemtype Directory -Path "$MAVEN_HOME_PARENT" -Force | Out-Null
|
||||
|
||||
# Download and Install Apache Maven
|
||||
Write-Verbose "Couldn't find MAVEN_HOME, downloading and installing it ..."
|
||||
Write-Verbose "Downloading from: $distributionUrl"
|
||||
Write-Verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName"
|
||||
|
||||
$webclient = New-Object System.Net.WebClient
|
||||
if ($env:MVNW_USERNAME -and $env:MVNW_PASSWORD) {
|
||||
$webclient.Credentials = New-Object System.Net.NetworkCredential($env:MVNW_USERNAME, $env:MVNW_PASSWORD)
|
||||
}
|
||||
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
|
||||
$webclient.DownloadFile($distributionUrl, "$TMP_DOWNLOAD_DIR/$distributionUrlName") | Out-Null
|
||||
|
||||
# If specified, validate the SHA-256 sum of the Maven distribution zip file
|
||||
$distributionSha256Sum = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionSha256Sum
|
||||
if ($distributionSha256Sum) {
|
||||
if ($USE_MVND) {
|
||||
Write-Error "Checksum validation is not supported for maven-mvnd. `nPlease disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties."
|
||||
}
|
||||
Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash
|
||||
if ((Get-FileHash "$TMP_DOWNLOAD_DIR/$distributionUrlName" -Algorithm SHA256).Hash.ToLower() -ne $distributionSha256Sum) {
|
||||
Write-Error "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised. If you updated your Maven version, you need to update the specified distributionSha256Sum property."
|
||||
}
|
||||
}
|
||||
|
||||
# unzip and move
|
||||
Expand-Archive "$TMP_DOWNLOAD_DIR/$distributionUrlName" -DestinationPath "$TMP_DOWNLOAD_DIR" | Out-Null
|
||||
Rename-Item -Path "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" -NewName $MAVEN_HOME_NAME | Out-Null
|
||||
try {
|
||||
Move-Item -Path "$TMP_DOWNLOAD_DIR/$MAVEN_HOME_NAME" -Destination $MAVEN_HOME_PARENT | Out-Null
|
||||
} catch {
|
||||
if (! (Test-Path -Path "$MAVEN_HOME" -PathType Container)) {
|
||||
Write-Error "fail to move MAVEN_HOME"
|
||||
}
|
||||
} finally {
|
||||
try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null }
|
||||
catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" }
|
||||
}
|
||||
|
||||
Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD"
|
||||
@@ -0,0 +1,72 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="https://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>com.example</groupId>
|
||||
<artifactId>test-spring-index-structure</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>3.4.3</version>
|
||||
<relativePath /> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
<jakarta-servlet.version>5.0.0</jakarta-servlet.version>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||
<java.version>17</java.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-validation</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-security</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<!--
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
<version>3.2.0</version>
|
||||
<configuration>
|
||||
<source>8</source>
|
||||
<detectJavaApiLink>false</detectJavaApiLink>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
-->
|
||||
</project>
|
||||
@@ -0,0 +1,5 @@
|
||||
package org.test;
|
||||
|
||||
public class BeanClass1 {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package org.test;
|
||||
|
||||
public class BeanClass2 {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package org.test;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
@SpringBootApplication
|
||||
public class MainClass {
|
||||
|
||||
@Value("server.port")
|
||||
private String serverport;
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
SpringApplication.run(MainClass.class, args);
|
||||
}
|
||||
|
||||
@Bean
|
||||
BeanClass1 bean1() {
|
||||
return new BeanClass1();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package org.test;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.web.bind.WebDataBinder;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.InitBinder;
|
||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
|
||||
|
||||
import jakarta.validation.Valid;
|
||||
|
||||
@RestController
|
||||
public class RestControllerExample {
|
||||
|
||||
@Autowired(required = false)
|
||||
private String serverPort;
|
||||
|
||||
@InitBinder
|
||||
public void setAllowedFields(WebDataBinder dataBinder) {
|
||||
dataBinder.setDisallowedFields("id");
|
||||
}
|
||||
|
||||
@ModelAttribute("owner")
|
||||
public String findOwner(@PathVariable(required = false) Integer ownerId) {
|
||||
return "example";
|
||||
}
|
||||
|
||||
@GetMapping("/owners/find")
|
||||
public String initFindForm() {
|
||||
return "owners/findOwners";
|
||||
}
|
||||
|
||||
@PostMapping("/owners/new")
|
||||
public String processCreationForm(String owner, BindingResult result, RedirectAttributes redirectAttributes) {
|
||||
if (result.hasErrors()) {
|
||||
redirectAttributes.addFlashAttribute("error", "There was an error in creating the owner.");
|
||||
return "something";
|
||||
}
|
||||
|
||||
return "redirect:/owners/";
|
||||
}
|
||||
|
||||
@RequestMapping(path = "/owners/{ownerId}/edit", method = RequestMethod.POST)
|
||||
public String processUpdateOwnerForm(@Valid String owner, BindingResult result, @PathVariable("ownerId") int ownerId) {
|
||||
return "redirect:/owners/{ownerId}";
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user