Skip to content

Commit

Permalink
Fix named beans detection and route builder class hierarchy. (#947)
Browse files Browse the repository at this point in the history
Co-authored-by: Adriano Machado <[email protected]>
  • Loading branch information
ammachado and ammachado authored Oct 22, 2023
1 parent 0c09a74 commit 1011555
Show file tree
Hide file tree
Showing 7 changed files with 134 additions and 118 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,10 @@ public abstract class AbstractCamelInspection extends LocalInspectionTool {

private boolean forceEnabled;

public AbstractCamelInspection() {
protected AbstractCamelInspection() {
}

public AbstractCamelInspection(boolean forceEnabled) {
protected AbstractCamelInspection(boolean forceEnabled) {
this.forceEnabled = forceEnabled;
}

Expand Down Expand Up @@ -119,7 +119,7 @@ private void validateSimple(@NotNull PsiElement element, final @NotNull Problems
CamelService camelService = element.getProject().getService(CamelService.class);

IElementType type = element.getNode().getElementType();
LOG.trace("Element " + element + " of type: " + type + " to inspect simple: " + text);
LOG.trace("Element %s of type: %s to inspect simple: %s".formatted(element, type, text));

try {
// need to use the classloader that can load classes from the camel-core
Expand All @@ -146,8 +146,8 @@ private void validateSimple(@NotNull PsiElement element, final @NotNull Problems
holder.registerProblem(element, msg);
}
}
} catch (Throwable e) {
LOG.warn("Error inspection Camel simple: " + text, e);
} catch (Exception e) {
LOG.warn("Error inspection Camel simple: %s".formatted(text), e);
}
}

Expand Down Expand Up @@ -189,8 +189,8 @@ private void validateJSonPath(@NotNull PsiElement element, final @NotNull Proble
holder.registerProblem(element, msg);
}
}
} catch (Throwable e) {
LOG.warn("Error inspection Camel jsonpath: " + text, e);
} catch (Exception e) {
LOG.warn("Error inspection Camel jsonpath: %s".formatted(text), e);
}
}

Expand All @@ -209,7 +209,7 @@ private void validateEndpoint(@NotNull PsiElement element, final @NotNull Proble

// camel catalog expects &amp; as & when it parses so replace all &amp; as &
String camelQuery = text;
camelQuery = camelQuery.replaceAll("&amp;", "&");
camelQuery = camelQuery.replace("&amp;", "&");

// strip up ending incomplete parameter
if (camelQuery.endsWith("&") || camelQuery.endsWith("?")) {
Expand All @@ -219,9 +219,7 @@ private void validateEndpoint(@NotNull PsiElement element, final @NotNull Proble
boolean stringFormat = camelIdeaUtils.isFromStringFormatEndpoint(element);
if (stringFormat) {
// if the node is fromF or toF, then replace all %X with {{%X}} as we cannot parse that value
camelQuery = camelQuery.replaceAll("%s", "\\{\\{\\%s\\}\\}");
camelQuery = camelQuery.replaceAll("%d", "\\{\\{\\%d\\}\\}");
camelQuery = camelQuery.replaceAll("%b", "\\{\\{\\%b\\}\\}");
camelQuery = camelQuery.replaceAll("(%[bds])", "{{$1}}");
}

boolean consumerOnly = camelIdeaUtils.isConsumerEndpoint(element);
Expand All @@ -238,8 +236,8 @@ private void validateEndpoint(@NotNull PsiElement element, final @NotNull Proble
extractSetValue(result, result.getUnknown(), text, element, holder, isOnTheFly, new AbstractCamelInspection.UnknownErrorMsg());
extractSetValue(result, result.getNotConsumerOnly(), text, element, holder, isOnTheFly, new AbstractCamelInspection.NotConsumerOnlyErrorMsg());
extractSetValue(result, result.getNotProducerOnly(), text, element, holder, isOnTheFly, new AbstractCamelInspection.NotProducerOnlyErrorMsg());
} catch (Throwable e) {
LOG.warn("Error inspecting Camel endpoint: " + text, e);
} catch (Exception e) {
LOG.warn("Error inspecting Camel endpoint: %s".formatted(text), e);
}
}

Expand Down Expand Up @@ -271,7 +269,7 @@ private static class BooleanErrorMsg implements CamelAnnotatorEndpointMessage<Ma
@Override
public String getErrorMessage(EndpointValidationResult result, Map.Entry<String, String> entry) {
String name = entry.getKey();
boolean empty = entry.getValue() == null || entry.getValue().length() == 0;
boolean empty = entry.getValue() == null || entry.getValue().isEmpty();
if (empty) {
return name + " has empty boolean value";
} else {
Expand Down Expand Up @@ -381,7 +379,6 @@ public boolean isWarnLevel() {
*
* @return the summary, or <tt>empty</tt> if no validation errors
*/
@SuppressWarnings("unchecked")
private <T> String summaryErrorMessage(EndpointValidationResult result, T entry, CamelAnnotatorEndpointMessage<T> msg) {
if (result.getIncapable() != null) {
return "Incapable of parsing uri: " + result.getIncapable();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,16 +74,14 @@
import java.awt.event.ActionEvent;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Supplier;

public class CamelDebuggerEvaluationDialog extends DialogWrapper {
public static final DataKey<CamelDebuggerEvaluationDialog> KEY = DataKey.create("CAMEL_DEBUGGER_EVALUATION_DIALOG");

//can not use new SHIFT_DOWN_MASK etc because in this case ActionEvent modifiers do not match
//cannot use new SHIFT_DOWN_MASK etc. because in this case ActionEvent modifiers do not match
private static final int ADD_WATCH_MODIFIERS = (SystemInfo.isMac ? InputEvent.META_MASK : InputEvent.CTRL_MASK) | InputEvent.SHIFT_MASK;
static KeyStroke addWatchKeyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, ADD_WATCH_MODIFIERS);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@
import com.intellij.psi.impl.source.PostprocessReformattingAspect;
import com.intellij.psi.impl.source.PsiClassReferenceType;
import com.intellij.psi.search.searches.ClassInheritorsSearch;
import com.intellij.psi.util.ClassUtil;
import com.intellij.psi.util.InheritanceUtil;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.testFramework.LightVirtualFile;
Expand Down Expand Up @@ -94,7 +93,7 @@ public class JavaCamelIdeaUtils extends CamelIdeaUtils implements CamelIdeaUtils
"kamelet", "step", "transacted", "saga", "route", "resequence", "policy", "onException", "onCompletion",
"from", "rest", "restConfiguration");
/**
* Name of the methods corresponding to root element of sub DSL.
* Name of the methods corresponding to the root element of sub DSL.
*/
private static final Set<String> SUB_DSL_ROOTS = Set.of("expression", "dataFormat");
/**
Expand Down Expand Up @@ -122,6 +121,23 @@ public class JavaCamelIdeaUtils extends CamelIdeaUtils implements CamelIdeaUtils
"org.apache.camel.spring.SpringRouteBuilder",
"org.apache.camel.builder.endpoint.EndpointRouteBuilder"
);
private static final List<String> BEAN_ANNOTATIONS = Arrays.asList(
"org.springframework.stereotype.Component",
"org.springframework.stereotype.Service",
"org.springframework.stereotype.Repository",
"javax.inject.Named",
"javax.inject.Singleton",
"javax.enterprise.context.ApplicationScoped",
"javax.enterprise.context.SessionScoped",
"javax.enterprise.context.ConversationScoped",
"javax.enterprise.context.RequestScoped",
"jakarta.inject.Named",
"jakarta.inject.Singleton",
"jakarta.enterprise.context.ApplicationScoped",
"jakarta.enterprise.context.SessionScoped",
"jakarta.enterprise.context.ConversationScoped",
"jakarta.enterprise.context.RequestScoped"
);

@Override
public boolean isCamelFile(PsiFile file) {
Expand Down Expand Up @@ -216,10 +232,10 @@ public boolean isCamelExpressionUsedAsPredicate(PsiElement element, String langu

// okay dive into the psi and find out which EIP are using the simple
PsiElement child = call.getFirstChild();
if (child instanceof PsiReferenceExpression) {
if (child instanceof PsiReferenceExpression psiReferenceExpression) {
// this code is needed as it may be used as a method call as a parameter and this requires
// a bit of psi code to unwrap the right elements.
PsiExpression exp = ((PsiReferenceExpression) child).getQualifierExpression();
PsiExpression exp = psiReferenceExpression.getQualifierExpression();
if (exp == null) {
// okay it was not a direct method call, so see if it was passed in as a parameter instead (expression list)
element = element.getParent();
Expand All @@ -230,8 +246,8 @@ public boolean isCamelExpressionUsedAsPredicate(PsiElement element, String langu
exp = PsiTreeUtil.getParentOfType(element.getParent(), PsiMethodCallExpression.class);
}
}
if (exp instanceof PsiMethodCallExpression) {
PsiMethod method = ((PsiMethodCallExpression) exp).resolveMethod();
if (exp instanceof PsiMethodCallExpression psiMethodCallExpression) {
PsiMethod method = psiMethodCallExpression.resolveMethod();
if (method != null) {
String name = method.getName();
return Arrays.asList(PREDICATE_EIPS).contains(name);
Expand Down Expand Up @@ -299,7 +315,7 @@ public boolean isFromStringFormatEndpoint(PsiElement element) {
public boolean acceptForAnnotatorOrInspection(PsiElement element) {
// skip XML limit on siblings
if (!IdeaUtils.getService().isFromFileType(element, "xml")) {
// for programming languages you can have complex structures with concat which we don't support yet
// for programming languages you can have complex structures with concat which we don't support it yet.
// we currently only support oneliner, so check how many siblings the element has (it has 1 with ending parenthesis which is okay)
return countSiblings(element) <= 1;
}
Expand All @@ -310,8 +326,8 @@ public boolean acceptForAnnotatorOrInspection(PsiElement element) {
public PsiClass getBeanClass(PsiElement element) {
final PsiElement beanPsiElement = getPsiElementForCamelBeanMethod(element);
if (beanPsiElement != null) {
if (beanPsiElement instanceof PsiClass) {
return (PsiClass) beanPsiElement;
if (beanPsiElement instanceof PsiClass psiClass) {
return psiClass;
}

PsiJavaCodeReferenceElement referenceElement = PsiTreeUtil.findChildOfType(beanPsiElement, PsiJavaCodeReferenceElement.class);
Expand Down Expand Up @@ -372,19 +388,22 @@ public boolean isPlaceForEndpointUri(PsiElement location) {
}

/**
* @return the {@link PsiClass} for the matching bean name by looking for classes annotated with spring Component, Service or Repository
* @return the {@link PsiClass} for the matching bean name by looking for classes annotated with
* Spring Component, Service or Repository or Quarkus javax or jakarta annotations.
*/
private Optional<PsiClass> searchForMatchingBeanClass(String beanName, Project project) {
final JavaClassUtils javaClassUtils = JavaClassUtils.getService();
return javaClassUtils.findBeanClassByName(beanName, "org.springframework.stereotype.Component", project)
.or(() -> javaClassUtils.findBeanClassByName(beanName, "org.springframework.stereotype.Service", project))
.or(() -> javaClassUtils.findBeanClassByName(beanName, "org.springframework.stereotype.Repository", project));

return BEAN_ANNOTATIONS
.stream()
.map(annotation -> javaClassUtils.findBeanClassByName(beanName, annotation, project))
.flatMap(Optional::stream)
.findFirst();
}

private List<PsiElement> findEndpoints(Module module, Predicate<String> uriCondition, Predicate<PsiLiteral> elementCondition) {
PsiManager manager = PsiManager.getInstance(module.getProject());
//TODO: use IdeaUtils.ROUTE_BUILDER_OR_EXPRESSION_CLASS_QUALIFIED_NAME somehow
PsiClass routeBuilderClass = ClassUtil.findPsiClass(manager, "org.apache.camel.builder.RouteBuilder");
PsiClass routeBuilderClass = IdeaUtils.findRouteBuilderClass(manager);

List<PsiElement> results = new ArrayList<>();
if (routeBuilderClass != null) {
Expand All @@ -394,8 +413,7 @@ private List<PsiElement> findEndpoints(Module module, Predicate<String> uriCondi
Collection<PsiLiteralExpression> literals = PsiTreeUtil.findChildrenOfType(routeBuilder, PsiLiteralExpression.class);
for (PsiLiteralExpression literal : literals) {
Object val = literal.getValue();
if (val instanceof String) {
String endpointUri = (String) val;
if (val instanceof String endpointUri) {
if (uriCondition.test(endpointUri) && elementCondition.test(literal)) {
results.add(literal);
}
Expand Down Expand Up @@ -436,7 +454,7 @@ private void format(PsiFile file, Document document, int startOffset, int endOff
final VirtualFile vFile = FileDocumentManager.getInstance().getFile(document);
if ((vFile == null || vFile instanceof LightVirtualFile) && !ApplicationManager.getApplication().isUnitTestMode()) {
// we assume that control flow reaches this place when the document is backed by a "virtual" file so any changes made by
// a formatter affect only PSI and it is out of sync with a document text
// a formatter affect only PSI, and it is out of sync with a document text
return;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import com.github.cameltooling.idea.reference.endpoint.CamelEndpoint;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.components.Service;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiClass;
Expand All @@ -34,13 +35,13 @@
import java.util.List;
import java.util.Objects;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/**
* Utility methods to work with Camel related {@link com.intellij.psi.PsiElement} elements.
* <p/>
* This class is only for Camel related IDEA APIs. If you need only IDEA APIs then use {@link IdeaUtils} instead.
*/
@Service
public final class CamelIdeaUtils implements Disposable {

public static final String[] CAMEL_FILE_EXTENSIONS = {"java", "xml", "yaml", "yml"};
Expand Down Expand Up @@ -192,7 +193,7 @@ public boolean isCamelExpressionOrLanguage(PsiClass clazz) {
}

/**
* Certain elements should be skipped for endpoint validation such as ActiveMQ brokerURL property and others.
* Certain elements should be skipped for endpoint validation, such as ActiveMQ brokerURL property and others.
*/
public boolean skipEndpointValidation(PsiElement element) {
return enabledExtensions.stream()
Expand Down Expand Up @@ -264,10 +265,9 @@ public List<PsiElement> findEndpointDeclarations(Module module, Predicate<String
.toList();
}


@Override
public void dispose() {

//noop
}

/**
Expand Down
Loading

0 comments on commit 1011555

Please sign in to comment.