adam-java-analytics
Version:
Java code analyzer for detecting package dependencies and circular references
138 lines (117 loc) • 4.2 kB
text/typescript
interface Package {
name: string
parent: string | null
hasChildren: boolean
}
interface Dependency {
source: string
target: string
}
interface ProjectData {
rootPackage: string
packages: Package[]
dependencies: Dependency[]
circularDependencies: string[][]
}
/**
* Analyzes a Java project to extract package and dependency information
*/
export async function analyzeProject(projectPath: string): Promise<ProjectData> {
try {
// This would be a server action in a real implementation
// For demo purposes, we'll return mock data
return getMockProjectData()
} catch (error) {
console.error("Error analyzing project:", error)
throw new Error("Failed to analyze project")
}
}
/**
* Detects circular dependencies in the project
*/
export function detectCircularDependencies(packages: Package[], dependencies: Dependency[]): string[][] {
const graph: Record<string, string[]> = {}
// Build adjacency list
packages.forEach((pkg) => {
graph[pkg.name] = []
})
dependencies.forEach((dep) => {
if (graph[dep.source]) {
graph[dep.source].push(dep.target)
}
})
const cycles: string[][] = []
const visited = new Set<string>()
const recursionStack = new Set<string>()
function dfs(node: string, path: string[] = []): void {
if (recursionStack.has(node)) {
// Found a cycle
const cycleStart = path.indexOf(node)
if (cycleStart !== -1) {
const cycle = path.slice(cycleStart).concat(node)
// Check if this cycle or its rotation is already in cycles
const isNewCycle = !cycles.some((existingCycle) => {
if (existingCycle.length !== cycle.length) return false
// Check all possible rotations
for (let i = 0; i < existingCycle.length; i++) {
const rotated = [...existingCycle.slice(i), ...existingCycle.slice(0, i)]
if (JSON.stringify(rotated) === JSON.stringify(cycle)) return true
}
return false
})
if (isNewCycle) {
cycles.push(cycle)
}
}
return
}
if (visited.has(node)) return
visited.add(node)
recursionStack.add(node)
path.push(node)
for (const neighbor of graph[node] || []) {
dfs(neighbor, [...path])
}
recursionStack.delete(node)
}
// Run DFS from each node
for (const node of Object.keys(graph)) {
if (!visited.has(node)) {
dfs(node)
}
}
return cycles
}
/**
* Returns mock project data for demonstration
*/
function getMockProjectData(): ProjectData {
const packages: Package[] = [
{ name: "com.example", parent: null, hasChildren: true },
{ name: "com.example.app", parent: "com.example", hasChildren: true },
{ name: "com.example.app.controller", parent: "com.example.app", hasChildren: false },
{ name: "com.example.app.service", parent: "com.example.app", hasChildren: false },
{ name: "com.example.app.repository", parent: "com.example.app", hasChildren: false },
{ name: "com.example.app.model", parent: "com.example.app", hasChildren: false },
{ name: "com.example.util", parent: "com.example", hasChildren: true },
{ name: "com.example.util.helper", parent: "com.example.util", hasChildren: false },
{ name: "com.example.util.formatter", parent: "com.example.util", hasChildren: false },
]
const dependencies: Dependency[] = [
{ source: "com.example.app.controller", target: "com.example.app.service" },
{ source: "com.example.app.service", target: "com.example.app.repository" },
{ source: "com.example.app.repository", target: "com.example.app.model" },
{ source: "com.example.app.model", target: "com.example.util.formatter" },
{ source: "com.example.util.formatter", target: "com.example.util.helper" },
// Circular dependency for demonstration
{ source: "com.example.app.service", target: "com.example.app.controller" },
{ source: "com.example.util.helper", target: "com.example.app.model" },
]
const circularDependencies = detectCircularDependencies(packages, dependencies)
return {
rootPackage: "com.example",
packages,
dependencies,
circularDependencies,
}
}