Skip to content

Commit

Permalink
Improve API of module detector.
Browse files Browse the repository at this point in the history
  • Loading branch information
uhafner committed Jan 8, 2025
1 parent 6d31ab4 commit e1d6cda
Show file tree
Hide file tree
Showing 15 changed files with 142 additions and 139 deletions.
10 changes: 5 additions & 5 deletions src/main/java/edu/hm/hafner/analysis/AbstractModuleDetector.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

import org.apache.commons.lang3.StringUtils;

import edu.hm.hafner.analysis.ModuleDetector.FileSystem;
import edu.hm.hafner.analysis.ModuleDetectorRunner.FileSystemFacade;

/**
* Abstract class for all Module Detectors.
Expand All @@ -15,7 +15,7 @@ abstract class AbstractModuleDetector {
static final String ALL_DIRECTORIES = "**/";
static final String PLUS = ", ";

private final FileSystem factory;
private final FileSystemFacade factory;

/**
* Collects all projects of a specific type.
Expand All @@ -32,8 +32,8 @@ abstract class AbstractModuleDetector {
*/
abstract String getPattern();

AbstractModuleDetector(final FileSystem fileSystem) {
factory = fileSystem;
AbstractModuleDetector(final FileSystemFacade fileSystemFacade) {
factory = fileSystemFacade;
}

void addMapping(final Map<String, String> mapping, final String fileName, final String suffix,
Expand All @@ -43,7 +43,7 @@ void addMapping(final Map<String, String> mapping, final String fileName, final
}
}

public FileSystem getFactory() {
public FileSystemFacade getFactory() {
return factory;
}
}
10 changes: 5 additions & 5 deletions src/main/java/edu/hm/hafner/analysis/AntModuleDetector.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,16 @@
import org.apache.commons.lang3.StringUtils;
import org.xml.sax.SAXException;

import edu.hm.hafner.analysis.ModuleDetector.FileSystem;
import edu.hm.hafner.analysis.ModuleDetectorRunner.FileSystemFacade;

/**
* Detects module names by parsing the name of a source file, the ANT build.xml.
*/
public class AntModuleDetector extends AbstractModuleDetector {
class AntModuleDetector extends AbstractModuleDetector {
static final String ANT_PROJECT = "build.xml";

AntModuleDetector(final FileSystem fileSystem) {
super(fileSystem);
AntModuleDetector(final FileSystemFacade fileSystemFacade) {
super(fileSystemFacade);
}

@Override
Expand All @@ -44,7 +44,7 @@ void collectProjects(final Map<String, String> mapping, final List<String> proje
*/
private String parseBuildXml(final String buildXml) {
try (var file = getFactory().open(buildXml)) {
var digester = new SecureDigester(ModuleDetector.class);
var digester = new SecureDigester(ModuleDetectorRunner.class);

digester.push(new StringBuilder());
var xPath = "project";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;

import edu.hm.hafner.analysis.ModuleDetector.FileSystem;
import edu.hm.hafner.analysis.ModuleDetectorRunner.FileSystemFacade;

/**
* Detects module names by parsing gradle source files.
*/
public class GradleModuleDetector extends AbstractModuleDetector {
class GradleModuleDetector extends AbstractModuleDetector {
static final String BUILD_GRADLE = "build.gradle";
static final String BUILD_GRADLE_KTS = "build.gradle.kts";
static final String SETTINGS_GRADLE = "settings.gradle";
Expand All @@ -29,8 +29,8 @@ public class GradleModuleDetector extends AbstractModuleDetector {
private static final Pattern RE_GRADLE_SET_PROJECT_NAME =
Pattern.compile("^\\s*rootProject\\.(name\\s*=|setName\\(?)\\s*['\"]([^'\"]*)['\"]\\)?");

GradleModuleDetector(final FileSystem fileSystem) {
super(fileSystem);
GradleModuleDetector(final FileSystemFacade fileSystemFacade) {
super(fileSystemFacade);
}

@Override
Expand Down
10 changes: 5 additions & 5 deletions src/main/java/edu/hm/hafner/analysis/MavenModuleDetector.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,16 @@
import org.apache.commons.lang3.StringUtils;
import org.xml.sax.SAXException;

import edu.hm.hafner.analysis.ModuleDetector.FileSystem;
import edu.hm.hafner.analysis.ModuleDetectorRunner.FileSystemFacade;

/**
* Detects module names by parsing the name of a source file, the Maven pom.xml.
*/
public class MavenModuleDetector extends AbstractModuleDetector {
class MavenModuleDetector extends AbstractModuleDetector {
static final String MAVEN_POM = "pom.xml";

MavenModuleDetector(final FileSystem fileSystem) {
super(fileSystem);
MavenModuleDetector(final FileSystemFacade fileSystemFacade) {
super(fileSystemFacade);
}

@Override
Expand Down Expand Up @@ -51,7 +51,7 @@ private String parsePom(final String pom) {
@SuppressWarnings("OverlyBroadCatchBlock")
private String parsePomAttribute(final String pom, final String tagName) {
try (var file = getFactory().open(pom)) {
var digester = new SecureDigester(ModuleDetector.class);
var digester = new SecureDigester(ModuleDetectorRunner.class);
digester.push(new StringBuilder());
digester.addCallMethod("project/" + tagName, "append", 0);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,36 +23,36 @@
* @author Ullrich Hafner
* @author Christoph Laeubrich (support for OSGi-Bundles)
*/
public class ModuleDetector {
public class ModuleDetectorRunner {
private static final String BACK_SLASH = "\\";
private static final String SLASH = "/";

/** A list of all module detectors (e.g. Maven) */
/** A list of all module detectors (e.g., Maven). */
private final List<AbstractModuleDetector> moduleDetectors;
/** The factory to create input streams with. */
private final FileSystem factory;
private final FileSystemFacade fileSystemFacade;
/** Maps file names to module names. */
private final Map<String, String> fileNameToModuleName;
/** Sorted list of file name prefixes. */
private final List<String> prefixes;

/**
* Creates a new instance of {@link ModuleDetector}.
* Creates a new instance of {@link ModuleDetectorRunner}.
*
* @param workspace
* the workspace to scan for Maven pom.xml or ant build.xml files
* @param fileSystem
* file system facade to find and load files with
* the workspace to scan for module files
* @param fileSystemFacade
* file system facade to find and load the files with
*/
public ModuleDetector(final Path workspace, final FileSystem fileSystem) {
factory = fileSystem;
public ModuleDetectorRunner(final Path workspace, final FileSystemFacade fileSystemFacade) {
this.fileSystemFacade = fileSystemFacade;

moduleDetectors = new ArrayList<>(Arrays.asList(
new AntModuleDetector(factory),
new GradleModuleDetector(factory),
new MavenModuleDetector(factory),
new OsgiModuleDetector(factory)
));
moduleDetectors = Arrays.asList(
new AntModuleDetector(this.fileSystemFacade),
new GradleModuleDetector(this.fileSystemFacade),
new MavenModuleDetector(this.fileSystemFacade),
new OsgiModuleDetector(this.fileSystemFacade)
);

fileNameToModuleName = createFilesToModuleMapping(workspace);
prefixes = new ArrayList<>(fileNameToModuleName.keySet());
Expand Down Expand Up @@ -111,7 +111,7 @@ private List<String> find(final Path path) {
List<String> absoluteFileNames = new ArrayList<>();

for (AbstractModuleDetector moduleDetector : moduleDetectors) {
var relativeFileNames = factory.find(path, moduleDetector.getPattern());
var relativeFileNames = fileSystemFacade.find(path, moduleDetector.getPattern());
for (String relativeFileName : relativeFileNames) {
var relativePath = normalizePath(relativeFileName);
if (relativePath.startsWith(SLASH)) {
Expand All @@ -133,7 +133,7 @@ private String normalizePath(final String fileName) {
/**
* Facade for file system operations. May be replaced by stubs in test cases.
*/
public interface FileSystem {
public interface FileSystemFacade {
/**
* Returns all file names that match the specified pattern.
*
Expand All @@ -144,8 +144,7 @@ public interface FileSystem {
*
* @return the found file names
*/
@SuppressWarnings("AvoidObjectArrays") // TODO: change to list in next major release
String[] find(Path root, String pattern);
List<String> find(Path root, String pattern);

/**
* Creates an {@link InputStream} from the specified filename.
Expand Down
24 changes: 16 additions & 8 deletions src/main/java/edu/hm/hafner/analysis/ModuleResolver.java
Original file line number Diff line number Diff line change
@@ -1,34 +1,42 @@
package edu.hm.hafner.analysis;

import java.util.List;
import java.util.stream.Collectors;

/**
* Resolves module names by reading and mapping module definitions (build.xml, pom.xml, or Manifest.mf files).
*
* @author Ullrich Hafner
*/
public class ModuleResolver {
private final ModuleDetectorRunner runner;

/**
* Creates a new instance of {@link ModuleResolver}.
*
* @param runner the module runner to use
*/
public ModuleResolver(final ModuleDetectorRunner runner) {
this.runner = runner;
}

/**
* Resolves absolute paths of the affected files of the specified set of issues.
* Resolves the absolute paths of all affected files referenced by the specified report.
*
* @param report
* the issues to resolve the paths
* @param detector
* the module detector to use
* the issues to resolve the paths for
*/
public void run(final Report report, final ModuleDetector detector) {
public void run(final Report report) {
List<Issue> issuesWithoutModule = report.stream()
.filter(issue -> !issue.hasModuleName())
.collect(Collectors.toList());
.toList();

if (issuesWithoutModule.isEmpty()) {
report.logInfo("-> all issues already have a valid module name");

return;
}

issuesWithoutModule.forEach(issue -> issue.setModuleName(detector.guessModuleName(issue.getAbsolutePath())));
issuesWithoutModule.forEach(issue -> issue.setModuleName(runner.guessModuleName(issue.getAbsolutePath())));
report.logInfo("-> resolved module names for %d issues", issuesWithoutModule.size());
}
}
8 changes: 4 additions & 4 deletions src/main/java/edu/hm/hafner/analysis/OsgiModuleDetector.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@

import org.apache.commons.lang3.StringUtils;

import edu.hm.hafner.analysis.ModuleDetector.FileSystem;
import edu.hm.hafner.analysis.ModuleDetectorRunner.FileSystemFacade;

/**
* Detects module names by parsing the name of OSGi specific source files.
*/
public class OsgiModuleDetector extends AbstractModuleDetector {
class OsgiModuleDetector extends AbstractModuleDetector {
static final String BUNDLE_PROPERTIES = "OSGI-INF/l10n/bundle.properties";
static final String OSGI_BUNDLE = "META-INF/MANIFEST.MF";
static final String PLUGIN_PROPERTIES = "plugin.properties";
Expand All @@ -24,8 +24,8 @@ public class OsgiModuleDetector extends AbstractModuleDetector {
private static final String BUNDLE_VENDOR = "Bundle-Vendor";
private static final String REPLACEMENT_CHAR = "%";

OsgiModuleDetector(final FileSystem fileSystem) {
super(fileSystem);
OsgiModuleDetector(final FileSystemFacade fileSystemFacade) {
super(fileSystemFacade);
}

@Override
Expand Down
13 changes: 6 additions & 7 deletions src/main/java/edu/hm/hafner/analysis/Severity.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,12 @@
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;

/**
* Severity of an issue. The predefined set of severities consists of an error and 3 warnings with priorities high,
* normal, or low. Additional severities can be created if this set of severities is not sufficient. Note that new
* instances are not cached by this class so that there might exist several severity instances with the same name.
* Severity of an issue. The predefined set of severities consists of an error and three warnings with priorities high,
* normal, or low. Additional severities can be created if this set of severities is not enough. Note that this class
* does not cache new instances so that there might exist several severity instances with the same name.
*
* @author Ullrich Hafner
*/
// FIXME: this class should be an enum
@Immutable
public class Severity implements Serializable {
@Serial
Expand Down Expand Up @@ -71,8 +70,8 @@ public static Severity valueOf(final String name) {
}

/**
* Converts a String severity to one of the predefined severities. If the provided String does not match then the default
* severity will be returned.
* Converts a String severity to one of the predefined severities. If the provided String does not match,
* then the default severity will be returned.
*
* @param severity
* priority as a String
Expand All @@ -92,7 +91,7 @@ public static Severity valueOf(@CheckForNull final String severity, final Severi

/**
* Converts a String severity to one of the predefined severities. If the provided String does not match (even
* partly) then the default severity will be returned.
* partly), then the default severity will be returned.
*
* @param severity
* the severity string
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Path;
import java.util.List;

import org.apache.commons.lang3.StringUtils;
import org.junit.jupiter.api.Test;

import edu.hm.hafner.analysis.ModuleDetector.FileSystem;
import edu.hm.hafner.analysis.ModuleDetectorRunner.FileSystemFacade;
import edu.hm.hafner.util.PathUtil;
import edu.hm.hafner.util.ResourceTest;

Expand All @@ -26,13 +27,11 @@ abstract class AbstractModuleDetectorTest extends ResourceTest {
@Test
void shouldIgnoreExceptionsDuringParsing() {
var fileSystem = createFileSystemStub(stub -> {
when(stub.find(any(), anyString())).thenReturn(new String[]{
getPathPrefix() + getProjectFileName()
});
when(stub.find(any(), anyString())).thenReturn(List.of(getPathPrefix() + getProjectFileName()));
when(stub.open(anyString())).thenThrow(new FileNotFoundException("File not found"));
});

var detector = new ModuleDetector(ROOT, fileSystem);
var detector = new ModuleDetectorRunner(ROOT, fileSystem);

assertThat(detector.guessModuleName(PREFIX + getFileName())).isEqualTo(StringUtils.EMPTY);
}
Expand All @@ -43,9 +42,9 @@ void shouldIgnoreExceptionsDuringParsing() {

abstract String getProjectFileName();

protected FileSystem createFileSystemStub(final Stub stub) {
protected FileSystemFacade createFileSystemStub(final Stub stub) {
try {
var fileSystem = mock(FileSystem.class);
var fileSystem = mock(FileSystemFacade.class);
stub.apply(fileSystem);
return fileSystem;
}
Expand All @@ -59,10 +58,10 @@ protected InputStream read(final String fileName) {
}

/**
* Stubs the {@link PackageDetectors.FileSystem} using a lambda.
* Stubs the {@link FileSystemFacade} using a lambda.
*/
@FunctionalInterface
protected interface Stub {
void apply(FileSystem f) throws IOException;
void apply(FileSystemFacade f) throws IOException;
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package edu.hm.hafner.analysis;

import java.util.List;

import org.apache.commons.lang3.StringUtils;
import org.junit.jupiter.api.Test;

Expand Down Expand Up @@ -31,10 +33,10 @@ String getProjectFileName() {
@Test
void shouldIdentifyModuleByReadingAntProjectFile() {
var factory = createFileSystemStub(stub -> {
when(stub.find(any(), anyString())).thenReturn(new String[]{PATH_PREFIX_ANT + AntModuleDetector.ANT_PROJECT});
when(stub.find(any(), anyString())).thenReturn(List.of(PATH_PREFIX_ANT + AntModuleDetector.ANT_PROJECT));
when(stub.open(anyString())).thenAnswer(filename -> read(AntModuleDetector.ANT_PROJECT));
});
var detector = new ModuleDetector(ROOT, factory);
var detector = new ModuleDetectorRunner(ROOT, factory);

assertThat(detector.guessModuleName(PREFIX + PATH_PREFIX_ANT + "something.txt"))
.isEqualTo(EXPECTED_ANT_MODULE);
Expand Down
Loading

0 comments on commit e1d6cda

Please sign in to comment.