-
-
Notifications
You must be signed in to change notification settings - Fork 290
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Initial rewrite of old structurizr-analysis library.
- Loading branch information
1 parent
ec75900
commit 51f9041
Showing
42 changed files
with
1,373 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,10 @@ | ||
rootProject.name = 'structurizr-java' | ||
|
||
include 'structurizr-inspection' | ||
include 'structurizr-autolayout' | ||
include 'structurizr-client' | ||
include 'structurizr-component' | ||
include 'structurizr-core' | ||
include 'structurizr-dsl' | ||
include 'structurizr-export' | ||
include 'structurizr-autolayout' | ||
include 'structurizr-import' | ||
include 'structurizr-inspection' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
# structurizr-component | ||
|
||
[![Maven Central](https://img.shields.io/maven-central/v/com.structurizr/structurizr-component.svg?label=Maven%20Central)](https://search.maven.org/artifact/com.structurizr/structurizr-component) | ||
|
||
This library provides a facility to discover components in a Java codebase, via a combination of | ||
[Apache Commons BCEL](https://commons.apache.org/proper/commons-bcel/) and [JavaParser](https://javaparser.org), | ||
using a pluggable and customisable set of matching and filtering rules. | ||
|
||
__Unreleased, experimental, and potentially subject to change - see tests for an example.__ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
dependencies { | ||
|
||
api project(':structurizr-core') | ||
implementation 'org.apache.bcel:bcel:6.8.1' | ||
implementation 'com.github.javaparser:javaparser-symbol-solver-core:3.26.1' | ||
|
||
testImplementation 'org.junit.jupiter:junit-jupiter:5.9.2' | ||
|
||
} | ||
|
||
description = 'Component finder for Java code' |
133 changes: 133 additions & 0 deletions
133
structurizr-component/src/main/java/com/structurizr/component/ComponentFinder.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
package com.structurizr.component; | ||
|
||
import com.structurizr.component.provider.TypeProvider; | ||
import com.structurizr.model.Component; | ||
import com.structurizr.model.Container; | ||
import com.structurizr.util.StringUtils; | ||
import org.apache.bcel.Repository; | ||
import org.apache.bcel.classfile.ConstantPool; | ||
import org.apache.bcel.classfile.Method; | ||
import org.apache.bcel.generic.*; | ||
|
||
import java.util.*; | ||
|
||
/** | ||
* Allows you to find components in a Java codebase based upon a set of pluggable and customisable rules. | ||
* Use the {@link ComponentFinderBuilder} to create an instance of this class. | ||
*/ | ||
public final class ComponentFinder { | ||
|
||
private static final String COMPONENT_TYPE_PROPERTY_NAME = "component.type"; | ||
private static final String COMPONENT_SOURCE_PROPERTY_NAME = "component.src"; | ||
|
||
private final TypeRepository typeRepository = new TypeRepository(); | ||
private final Container container; | ||
private final List<ComponentFinderStrategy> componentFinderStrategies = new ArrayList<>(); | ||
|
||
ComponentFinder(Container container, Collection<TypeProvider> typeProviders, List<ComponentFinderStrategy> componentFinderStrategies) { | ||
if (container == null) { | ||
throw new IllegalArgumentException("A container must be specified."); | ||
} | ||
|
||
this.container = container; | ||
|
||
for (TypeProvider typeProvider : typeProviders) { | ||
Set<com.structurizr.component.Type> types = typeProvider.getTypes(); | ||
for (com.structurizr.component.Type type : types) { | ||
if (type.getJavaClass() != null) { | ||
// this is the BCEL identified type | ||
typeRepository.add(type); | ||
} else { | ||
// this is the source code identified type | ||
com.structurizr.component.Type bcelType = typeRepository.getType(type.getFullyQualifiedName()); | ||
if (bcelType != null) { | ||
bcelType.setDescription(type.getDescription()); | ||
bcelType.setSource(type.getSource()); | ||
} | ||
} | ||
} | ||
} | ||
|
||
Repository.clearCache(); | ||
for (com.structurizr.component.Type type : typeRepository.getTypes()) { | ||
if (type.getJavaClass() != null) { | ||
Repository.addClass(type.getJavaClass()); | ||
findDependencies(type); | ||
} | ||
} | ||
|
||
this.componentFinderStrategies.addAll(componentFinderStrategies); | ||
} | ||
|
||
private void findDependencies(com.structurizr.component.Type type) { | ||
ConstantPool cp = type.getJavaClass().getConstantPool(); | ||
ConstantPoolGen cpg = new ConstantPoolGen(cp); | ||
for (Method m : type.getJavaClass().getMethods()) { | ||
MethodGen mg = new MethodGen(m, type.getJavaClass().getClassName(), cpg); | ||
InstructionList il = mg.getInstructionList(); | ||
if (il == null) { | ||
continue; | ||
} | ||
|
||
InstructionHandle[] instructionHandles = il.getInstructionHandles(); | ||
for (InstructionHandle instructionHandle : instructionHandles) { | ||
Instruction instruction = instructionHandle.getInstruction(); | ||
if (!(instruction instanceof InvokeInstruction)) { | ||
continue; | ||
} | ||
|
||
InvokeInstruction invokeInstruction = (InvokeInstruction)instruction; | ||
ReferenceType referenceType = invokeInstruction.getReferenceType(cpg); | ||
if (!(referenceType instanceof ObjectType)) { | ||
continue; | ||
} | ||
|
||
ObjectType objectType = (ObjectType)referenceType; | ||
String referencedClassName = objectType.getClassName(); | ||
com.structurizr.component.Type referencedType = typeRepository.getType(referencedClassName); | ||
if (referencedType != null) { | ||
type.addDependency(referencedType); | ||
} | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* Find components, using all configured rules, in the order they were added. | ||
*/ | ||
public void findComponents() { | ||
Set<DiscoveredComponent> discoveredComponents = new HashSet<>(); | ||
Map<DiscoveredComponent, Component> componentMap = new HashMap<>(); | ||
|
||
for (ComponentFinderStrategy componentFinderStrategy : componentFinderStrategies) { | ||
discoveredComponents.addAll(componentFinderStrategy.findComponents(typeRepository)); | ||
} | ||
|
||
for (DiscoveredComponent discoveredComponent : discoveredComponents) { | ||
Component component = container.addComponent(discoveredComponent.getName()); | ||
component.addProperty(COMPONENT_TYPE_PROPERTY_NAME, discoveredComponent.getPrimaryType().getFullyQualifiedName()); | ||
if (!StringUtils.isNullOrEmpty(discoveredComponent.getPrimaryType().getSource())) { | ||
component.addProperty(COMPONENT_SOURCE_PROPERTY_NAME, discoveredComponent.getPrimaryType().getSource()); | ||
} | ||
component.setDescription(discoveredComponent.getDescription()); | ||
component.setTechnology(discoveredComponent.getTechnology()); | ||
componentMap.put(discoveredComponent, component); | ||
} | ||
|
||
// find dependencies between all components | ||
for (DiscoveredComponent discoveredComponent : discoveredComponents) { | ||
Set<com.structurizr.component.Type> typeDependencies = discoveredComponent.getAllDependencies(); | ||
for (Type typeDependency : typeDependencies) { | ||
for (DiscoveredComponent c : discoveredComponents) { | ||
if (c != discoveredComponent) { | ||
if (c.getAllTypes().contains(typeDependency)) { | ||
Component componentDependency = componentMap.get(c); | ||
componentMap.get(discoveredComponent).uses(componentDependency, ""); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
} |
68 changes: 68 additions & 0 deletions
68
structurizr-component/src/main/java/com/structurizr/component/ComponentFinderBuilder.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
package com.structurizr.component; | ||
|
||
import com.structurizr.component.provider.DirectoryTypeProvider; | ||
import com.structurizr.component.provider.JarFileTypeProvider; | ||
import com.structurizr.component.provider.SourceCodeTypeProvider; | ||
import com.structurizr.component.provider.TypeProvider; | ||
import com.structurizr.model.Container; | ||
|
||
import java.io.File; | ||
import java.util.ArrayList; | ||
import java.util.List; | ||
|
||
/** | ||
* Provides a way to create a {@link ComponentFinder} instance. | ||
*/ | ||
public class ComponentFinderBuilder { | ||
|
||
private Container container; | ||
private final List<TypeProvider> typeProviders = new ArrayList<>(); | ||
private final List<ComponentFinderStrategy> componentFinderStrategies = new ArrayList<>(); | ||
|
||
public ComponentFinderBuilder forContainer(Container container) { | ||
this.container = container; | ||
|
||
return this; | ||
} | ||
|
||
public ComponentFinderBuilder fromJarFile(String filename) { | ||
return fromJarFile(new File(filename)); | ||
} | ||
|
||
public ComponentFinderBuilder fromJarFile(File file) { | ||
this.typeProviders.add(new JarFileTypeProvider(file)); | ||
|
||
return this; | ||
} | ||
|
||
public ComponentFinderBuilder fromDirectory(String path) { | ||
return fromDirectory(new File(path)); | ||
} | ||
|
||
public ComponentFinderBuilder fromDirectory(File path) { | ||
this.typeProviders.add(new DirectoryTypeProvider(path)); | ||
|
||
return this; | ||
} | ||
|
||
public ComponentFinderBuilder fromSourceCode(String path) { | ||
return fromSourceCode(new File(path)); | ||
} | ||
|
||
public ComponentFinderBuilder fromSourceCode(File path) { | ||
this.typeProviders.add(new SourceCodeTypeProvider(path)); | ||
|
||
return this; | ||
} | ||
|
||
public ComponentFinderBuilder withStrategy(ComponentFinderStrategy componentFinderStrategy) { | ||
this.componentFinderStrategies.add(componentFinderStrategy); | ||
|
||
return this; | ||
} | ||
|
||
public ComponentFinder build() { | ||
return new ComponentFinder(container, typeProviders, componentFinderStrategies); | ||
} | ||
|
||
} |
54 changes: 54 additions & 0 deletions
54
structurizr-component/src/main/java/com/structurizr/component/ComponentFinderStrategy.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
package com.structurizr.component; | ||
|
||
import com.structurizr.component.filter.TypeFilter; | ||
import com.structurizr.component.matcher.TypeMatcher; | ||
import com.structurizr.component.naming.NamingStrategy; | ||
import com.structurizr.component.supporting.SupportingTypesStrategy; | ||
|
||
import java.util.HashSet; | ||
import java.util.Set; | ||
|
||
/** | ||
* A component finder strategy is a wrapper for a combination of the following: | ||
* - {@link TypeMatcher} | ||
* - {@link TypeFilter} | ||
* - {@link SupportingTypesStrategy} | ||
* - {@link NamingStrategy} | ||
* | ||
* Use the {@link ComponentFinderStrategyBuilder} to create an instance of this class. | ||
*/ | ||
class ComponentFinderStrategy { | ||
|
||
private final TypeMatcher typeMatcher; | ||
private final TypeFilter typeFilter; | ||
private final SupportingTypesStrategy supportingTypesStrategy; | ||
private final NamingStrategy namingStrategy; | ||
|
||
ComponentFinderStrategy(TypeMatcher typeMatcher, TypeFilter typeFilter, SupportingTypesStrategy supportingTypesStrategy, NamingStrategy namingStrategy) { | ||
this.typeMatcher = typeMatcher; | ||
this.typeFilter = typeFilter; | ||
this.supportingTypesStrategy = supportingTypesStrategy; | ||
this.namingStrategy = namingStrategy; | ||
} | ||
|
||
Set<DiscoveredComponent> findComponents(TypeRepository typeRepository) { | ||
Set<DiscoveredComponent> components = new HashSet<>(); | ||
|
||
Set<Type> types = typeRepository.getTypes(); | ||
for (Type type : types) { | ||
if (typeMatcher.matches(type) && typeFilter.accept(type)) { | ||
DiscoveredComponent component = new DiscoveredComponent(namingStrategy.nameOf(type), type); | ||
component.setDescription(type.getDescription()); | ||
component.setTechnology(typeMatcher.getTechnology()); | ||
components.add(component); | ||
|
||
// now find supporting types | ||
Set<Type> supportingTypes = supportingTypesStrategy.findSupportingTypes(type); | ||
component.addSupportingTypes(supportingTypes); | ||
} | ||
} | ||
|
||
return components; | ||
} | ||
|
||
} |
56 changes: 56 additions & 0 deletions
56
...izr-component/src/main/java/com/structurizr/component/ComponentFinderStrategyBuilder.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
package com.structurizr.component; | ||
|
||
import com.structurizr.component.filter.DefaultTypeFilter; | ||
import com.structurizr.component.filter.TypeFilter; | ||
import com.structurizr.component.matcher.TypeMatcher; | ||
import com.structurizr.component.naming.DefaultNamingStrategy; | ||
import com.structurizr.component.naming.NamingStrategy; | ||
import com.structurizr.component.supporting.DefaultSupportingTypesStrategy; | ||
import com.structurizr.component.supporting.SupportingTypesStrategy; | ||
|
||
/** | ||
* Provides a way to create a {@link ComponentFinderStrategy} instance. | ||
*/ | ||
public final class ComponentFinderStrategyBuilder { | ||
|
||
private TypeMatcher typeMatcher; | ||
private TypeFilter typeFilter = new DefaultTypeFilter(); | ||
private SupportingTypesStrategy supportingTypesStrategy = new DefaultSupportingTypesStrategy(); | ||
private NamingStrategy namingStrategy = new DefaultNamingStrategy(); | ||
|
||
public ComponentFinderStrategyBuilder() { | ||
} | ||
|
||
public ComponentFinderStrategyBuilder matchedBy(TypeMatcher typeMatcher) { | ||
this.typeMatcher = typeMatcher; | ||
|
||
return this; | ||
} | ||
|
||
public ComponentFinderStrategyBuilder filteredBy(TypeFilter typeFilter) { | ||
this.typeFilter = typeFilter; | ||
|
||
return this; | ||
} | ||
|
||
public ComponentFinderStrategyBuilder supportedBy(SupportingTypesStrategy supportingTypesStrategy) { | ||
this.supportingTypesStrategy = supportingTypesStrategy; | ||
|
||
return this; | ||
} | ||
|
||
public ComponentFinderStrategyBuilder namedBy(NamingStrategy namingStrategy) { | ||
this.namingStrategy = namingStrategy; | ||
|
||
return this; | ||
} | ||
|
||
public ComponentFinderStrategy build() { | ||
if (typeMatcher == null) { | ||
throw new RuntimeException("A type matcher must be specified"); | ||
} | ||
|
||
return new ComponentFinderStrategy(typeMatcher, typeFilter, supportingTypesStrategy, namingStrategy); | ||
} | ||
|
||
} |
Oops, something went wrong.