Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package io.github.randomcodespace.iq.detector;

import io.github.randomcodespace.iq.model.CodeEdge;
import io.github.randomcodespace.iq.model.CodeNode;
import io.github.randomcodespace.iq.model.EdgeKind;
import io.github.randomcodespace.iq.model.NodeKind;

import java.util.List;

/**
* Shared helper methods for language structure detectors (Scala, Kotlin, etc.)
* that detect classes, interfaces, objects, methods, and imports using regex.
*/
public final class StructuresDetectorHelper {

private StructuresDetectorHelper() {}

/**
* Create and add an IMPORTS edge.
*/
public static void addImportEdge(String filePath, String target, List<CodeEdge> edges) {
CodeEdge e = new CodeEdge();
e.setId(filePath + ":imports:" + target);
e.setKind(EdgeKind.IMPORTS);
e.setSourceId(filePath);
e.setTarget(new CodeNode(target, NodeKind.MODULE, target));
edges.add(e);
}

/**
* Create a code node with standard fields for structure detection.
*/
public static CodeNode createStructureNode(String filePath, String name, NodeKind kind, int lineStart) {
CodeNode n = new CodeNode();
n.setId(filePath + ":" + name);
n.setKind(kind);
n.setLabel(name);
n.setFqn(name);
n.setFilePath(filePath);
n.setLineStart(lineStart);
return n;
}

/**
* Create and add an EXTENDS edge.
*/
public static void addExtendsEdge(String sourceNodeId, String targetName, NodeKind targetKind,
List<CodeEdge> edges) {
CodeEdge e = new CodeEdge();
e.setId(sourceNodeId + ":extends:" + targetName);
e.setKind(EdgeKind.EXTENDS);
e.setSourceId(sourceNodeId);
e.setTarget(new CodeNode(targetName, targetKind, targetName));
edges.add(e);
}

/**
* Create and add an IMPLEMENTS edge.
*/
public static void addImplementsEdge(String sourceNodeId, String targetName, List<CodeEdge> edges) {
CodeEdge e = new CodeEdge();
e.setId(sourceNodeId + ":implements:" + targetName);
e.setKind(EdgeKind.IMPLEMENTS);
e.setSourceId(sourceNodeId);
e.setTarget(new CodeNode(targetName, NodeKind.INTERFACE, targetName));
edges.add(e);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -71,15 +71,8 @@
String selector = m.group(1);
String className = m.group(2);
if (!seen.add(className)) continue;
int line = text.substring(0, m.start()).split("\n", -1).length;
CodeNode node = new CodeNode();
node.setId("angular:" + filePath + ":component:" + className);
node.setKind(NodeKind.COMPONENT);
node.setLabel(className);
node.setFqn(filePath + "::" + className);
node.setFilePath(filePath);
node.setLineStart(line);
node.getProperties().put("framework", "angular");
CodeNode node = FrontendDetectorHelper.createComponentNode("angular", filePath, "component",

Check failure on line 74 in src/main/java/io/github/randomcodespace/iq/detector/frontend/AngularComponentDetector.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Define a constant instead of duplicating this literal "angular" 5 times.

See more on https://sonarcloud.io/project/issues?id=RandomCodeSpace_code-iq&issues=AZ1ZJ8spV80h1QdmSSRd&open=AZ1ZJ8spV80h1QdmSSRd&pullRequest=38

Check failure on line 74 in src/main/java/io/github/randomcodespace/iq/detector/frontend/AngularComponentDetector.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Define a constant instead of duplicating this literal "component" 4 times.

See more on https://sonarcloud.io/project/issues?id=RandomCodeSpace_code-iq&issues=AZ1ZJ8spV80h1QdmSSRe&open=AZ1ZJ8spV80h1QdmSSRe&pullRequest=38
className, NodeKind.COMPONENT, FrontendDetectorHelper.lineAt(text, m.start()));
node.getProperties().put("selector", selector);
node.getProperties().put("decorator", "Component");
nodes.add(node);
Expand All @@ -91,15 +84,8 @@
String providedIn = m.group(1);
String className = m.group(2);
if (!seen.add(className)) continue;
int line = text.substring(0, m.start()).split("\n", -1).length;
CodeNode node = new CodeNode();
node.setId("angular:" + filePath + ":service:" + className);
node.setKind(NodeKind.MIDDLEWARE);
node.setLabel(className);
node.setFqn(filePath + "::" + className);
node.setFilePath(filePath);
node.setLineStart(line);
node.getProperties().put("framework", "angular");
CodeNode node = FrontendDetectorHelper.createComponentNode("angular", filePath, "service",
className, NodeKind.MIDDLEWARE, FrontendDetectorHelper.lineAt(text, m.start()));
node.getProperties().put("provided_in", providedIn);
node.getProperties().put("decorator", "Injectable");
nodes.add(node);
Expand All @@ -111,15 +97,8 @@
String selector = m.group(1);
String className = m.group(2);
if (!seen.add(className)) continue;
int line = text.substring(0, m.start()).split("\n", -1).length;
CodeNode node = new CodeNode();
node.setId("angular:" + filePath + ":component:" + className);
node.setKind(NodeKind.COMPONENT);
node.setLabel(className);
node.setFqn(filePath + "::" + className);
node.setFilePath(filePath);
node.setLineStart(line);
node.getProperties().put("framework", "angular");
CodeNode node = FrontendDetectorHelper.createComponentNode("angular", filePath, "component",
className, NodeKind.COMPONENT, FrontendDetectorHelper.lineAt(text, m.start()));
node.getProperties().put("selector", selector);
node.getProperties().put("decorator", "Directive");
nodes.add(node);
Expand All @@ -131,15 +110,8 @@
String pipeName = m.group(1);
String className = m.group(2);
if (!seen.add(className)) continue;
int line = text.substring(0, m.start()).split("\n", -1).length;
CodeNode node = new CodeNode();
node.setId("angular:" + filePath + ":component:" + className);
node.setKind(NodeKind.COMPONENT);
node.setLabel(className);
node.setFqn(filePath + "::" + className);
node.setFilePath(filePath);
node.setLineStart(line);
node.getProperties().put("framework", "angular");
CodeNode node = FrontendDetectorHelper.createComponentNode("angular", filePath, "component",
className, NodeKind.COMPONENT, FrontendDetectorHelper.lineAt(text, m.start()));
node.getProperties().put("pipe_name", pipeName);
node.getProperties().put("decorator", "Pipe");
nodes.add(node);
Expand All @@ -150,15 +122,8 @@
while (m.find()) {
String className = m.group(1);
if (!seen.add(className)) continue;
int line = text.substring(0, m.start()).split("\n", -1).length;
CodeNode node = new CodeNode();
node.setId("angular:" + filePath + ":component:" + className);
node.setKind(NodeKind.COMPONENT);
node.setLabel(className);
node.setFqn(filePath + "::" + className);
node.setFilePath(filePath);
node.setLineStart(line);
node.getProperties().put("framework", "angular");
CodeNode node = FrontendDetectorHelper.createComponentNode("angular", filePath, "component",
className, NodeKind.COMPONENT, FrontendDetectorHelper.lineAt(text, m.start()));
node.getProperties().put("decorator", "NgModule");
nodes.add(node);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package io.github.randomcodespace.iq.detector.frontend;

import io.github.randomcodespace.iq.model.CodeNode;
import io.github.randomcodespace.iq.model.NodeKind;

/**
* Shared helper methods for frontend component detectors (Angular, React, Vue).
* Provides common node creation patterns for components and hooks.
*/
final class FrontendDetectorHelper {

private FrontendDetectorHelper() {}

/**
* Create a frontend component node with standard fields.
*
* @param framework framework name (e.g. "angular", "react", "vue")
* @param filePath source file path
* @param idType node type for ID (e.g. "component", "service")
* @param className the component class/function name
* @param kind the node kind (COMPONENT, HOOK, MIDDLEWARE)
* @param line 1-based line number
* @return the configured CodeNode
*/
static CodeNode createComponentNode(String framework, String filePath, String idType,
String className, NodeKind kind, int line) {
CodeNode node = new CodeNode();
node.setId(framework + ":" + filePath + ":" + idType + ":" + className);
node.setKind(kind);
node.setLabel(className);
node.setFqn(filePath + "::" + className);
node.setFilePath(filePath);
node.setLineStart(line);
node.getProperties().put("framework", framework);
return node;
}

/**
* Calculate the 1-based line number from a character offset in text.
*/
static int lineAt(String text, int offset) {
return text.substring(0, offset).split("\n", -1).length;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
}

@Override
public DetectorResult detect(DetectorContext ctx) {

Check warning on line 49 in src/main/java/io/github/randomcodespace/iq/detector/frontend/ReactComponentDetector.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

A "Brain Method" was detected. Refactor it to reduce at least one of the following metrics: LOC from 79 to 64, Complexity from 17 to 14, Nesting Level from 3 to 2, Number of Variables from 35 to 6.

See more on https://sonarcloud.io/project/issues?id=RandomCodeSpace_code-iq&issues=AZ1ZJ8s-V80h1QdmSSRg&open=AZ1ZJ8s-V80h1QdmSSRg&pullRequest=38
String text = ctx.content();
if (text == null || text.isEmpty()) {
return DetectorResult.empty();
Expand All @@ -67,16 +67,9 @@
while (m.find()) {
String name = m.group(1);
if (componentNames.contains(name)) continue;
int line = text.substring(0, m.start()).split("\n", -1).length;
String sourceId = "react:" + filePath + ":component:" + name;
CodeNode node = new CodeNode();
node.setId(sourceId);
node.setKind(NodeKind.COMPONENT);
node.setLabel(name);
node.setFqn(filePath + "::" + name);
node.setFilePath(filePath);
node.setLineStart(line);
node.getProperties().put("framework", "react");
CodeNode node = FrontendDetectorHelper.createComponentNode("react", filePath, "component",

Check failure on line 71 in src/main/java/io/github/randomcodespace/iq/detector/frontend/ReactComponentDetector.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Define a constant instead of duplicating this literal "react" 3 times.

See more on https://sonarcloud.io/project/issues?id=RandomCodeSpace_code-iq&issues=AZ1ZJ8s-V80h1QdmSSRf&open=AZ1ZJ8s-V80h1QdmSSRf&pullRequest=38
name, NodeKind.COMPONENT, FrontendDetectorHelper.lineAt(text, m.start()));
node.getProperties().put("component_type", "function");
nodes.add(node);
componentNames.add(name);
Expand All @@ -90,16 +83,9 @@
while (m.find()) {
String name = m.group(1);
if (componentNames.contains(name)) continue;
int line = text.substring(0, m.start()).split("\n", -1).length;
String sourceId = "react:" + filePath + ":component:" + name;
CodeNode node = new CodeNode();
node.setId(sourceId);
node.setKind(NodeKind.COMPONENT);
node.setLabel(name);
node.setFqn(filePath + "::" + name);
node.setFilePath(filePath);
node.setLineStart(line);
node.getProperties().put("framework", "react");
CodeNode node = FrontendDetectorHelper.createComponentNode("react", filePath, "component",
name, NodeKind.COMPONENT, FrontendDetectorHelper.lineAt(text, m.start()));
node.getProperties().put("component_type", "class");
nodes.add(node);
componentNames.add(name);
Expand All @@ -114,16 +100,8 @@
while (m.find()) {
String name = m.group(1);
if (hookNames.contains(name)) continue;
int line = text.substring(0, m.start()).split("\n", -1).length;
CodeNode node = new CodeNode();
node.setId("react:" + filePath + ":hook:" + name);
node.setKind(NodeKind.HOOK);
node.setLabel(name);
node.setFqn(filePath + "::" + name);
node.setFilePath(filePath);
node.setLineStart(line);
node.getProperties().put("framework", "react");
nodes.add(node);
nodes.add(FrontendDetectorHelper.createComponentNode("react", filePath, "hook",
name, NodeKind.HOOK, FrontendDetectorHelper.lineAt(text, m.start())));
hookNames.add(name);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,15 +68,8 @@
while (m.find()) {
String name = m.group(1);
if (componentNames.contains(name)) continue;
int line = text.substring(0, m.start()).split("\n", -1).length;
CodeNode node = new CodeNode();
node.setId("vue:" + filePath + ":component:" + name);
node.setKind(NodeKind.COMPONENT);
node.setLabel(name);
node.setFqn(filePath + "::" + name);
node.setFilePath(filePath);
node.setLineStart(line);
node.getProperties().put("framework", "vue");
CodeNode node = FrontendDetectorHelper.createComponentNode("vue", filePath, "component",

Check failure on line 71 in src/main/java/io/github/randomcodespace/iq/detector/frontend/VueComponentDetector.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Define a constant instead of duplicating this literal "component" 3 times.

See more on https://sonarcloud.io/project/issues?id=RandomCodeSpace_code-iq&issues=AZ1ZJ8sWV80h1QdmSSRc&open=AZ1ZJ8sWV80h1QdmSSRc&pullRequest=38
name, NodeKind.COMPONENT, FrontendDetectorHelper.lineAt(text, m.start()));
node.getProperties().put("api_style", "composition");
nodes.add(node);
componentNames.add(name);
Expand All @@ -87,15 +80,8 @@
while (m.find()) {
String name = m.group(1);
if (componentNames.contains(name)) continue;
int line = text.substring(0, m.start()).split("\n", -1).length;
CodeNode node = new CodeNode();
node.setId("vue:" + filePath + ":component:" + name);
node.setKind(NodeKind.COMPONENT);
node.setLabel(name);
node.setFqn(filePath + "::" + name);
node.setFilePath(filePath);
node.setLineStart(line);
node.getProperties().put("framework", "vue");
CodeNode node = FrontendDetectorHelper.createComponentNode("vue", filePath, "component",
name, NodeKind.COMPONENT, FrontendDetectorHelper.lineAt(text, m.start()));
node.getProperties().put("api_style", "options");
nodes.add(node);
componentNames.add(name);
Expand All @@ -106,15 +92,8 @@
while (m.find()) {
String compName = extractScriptSetupName(filePath);
if (compName == null || componentNames.contains(compName)) continue;
int line = text.substring(0, m.start()).split("\n", -1).length;
CodeNode node = new CodeNode();
node.setId("vue:" + filePath + ":component:" + compName);
node.setKind(NodeKind.COMPONENT);
node.setLabel(compName);
node.setFqn(filePath + "::" + compName);
node.setFilePath(filePath);
node.setLineStart(line);
node.getProperties().put("framework", "vue");
CodeNode node = FrontendDetectorHelper.createComponentNode("vue", filePath, "component",
compName, NodeKind.COMPONENT, FrontendDetectorHelper.lineAt(text, m.start()));
node.getProperties().put("api_style", "script_setup");
nodes.add(node);
componentNames.add(compName);
Expand All @@ -127,16 +106,8 @@
while (hm.find()) {
String name = hm.group(1);
if (hookNames.contains(name)) continue;
int line = text.substring(0, hm.start()).split("\n", -1).length;
CodeNode node = new CodeNode();
node.setId("vue:" + filePath + ":hook:" + name);
node.setKind(NodeKind.HOOK);
node.setLabel(name);
node.setFqn(filePath + "::" + name);
node.setFilePath(filePath);
node.setLineStart(line);
node.getProperties().put("framework", "vue");
nodes.add(node);
nodes.add(FrontendDetectorHelper.createComponentNode("vue", filePath, "hook",
name, NodeKind.HOOK, FrontendDetectorHelper.lineAt(text, hm.start())));
hookNames.add(name);
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package io.github.randomcodespace.iq.detector.java;

import io.github.randomcodespace.iq.detector.AbstractRegexDetector;
import io.github.randomcodespace.iq.model.CodeEdge;
import io.github.randomcodespace.iq.model.CodeNode;
import io.github.randomcodespace.iq.model.EdgeKind;
import io.github.randomcodespace.iq.model.NodeKind;

import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
* Shared base class for Java messaging detectors (JMS, RabbitMQ, IBM MQ, TIBCO EMS).
* Provides common patterns for class name extraction and edge creation.
*/
public abstract class AbstractJavaMessagingDetector extends AbstractRegexDetector {

protected static final Pattern CLASS_RE = Pattern.compile("(?:public\\s+)?class\\s+(\\w+)");

/**
* Extract the first class name from the source text.
* Returns null if no class is found.
*/
protected String extractClassName(String text) {
String[] lines = text.split("\n", -1);
for (String line : lines) {
Matcher cm = CLASS_RE.matcher(line);
if (cm.find()) {
return cm.group(1);
}
}
return null;
}

/**
* Create and add a messaging edge (CONSUMES, PRODUCES, SENDS_TO, RECEIVES_FROM, etc.).
*/
protected void addMessagingEdge(String sourceId, String targetId, EdgeKind kind, String label,
Map<String, Object> props, List<CodeEdge> edges) {
CodeEdge edge = new CodeEdge();
edge.setId(sourceId + "->" + kind.getValue() + "->" + targetId);
edge.setKind(kind);
edge.setSourceId(sourceId);
edge.setTarget(new CodeNode(targetId, NodeKind.QUEUE, label));
edge.setProperties(new LinkedHashMap<>(props));
edges.add(edge);
}
}
Loading
Loading