-
Notifications
You must be signed in to change notification settings - Fork 59
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1237 from uhafner/package-detectors
Import package detector from analysis-model
- Loading branch information
Showing
13 changed files
with
359 additions
and
0 deletions.
There are no files selected for viewing
27 changes: 27 additions & 0 deletions
27
src/main/java/edu/hm/hafner/util/CSharpNamespaceDetector.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,27 @@ | ||
package edu.hm.hafner.util; | ||
|
||
import java.util.regex.Pattern; | ||
|
||
/** | ||
* Detects the namespace of a C# workspace file. | ||
* | ||
* @author Ullrich Hafner | ||
*/ | ||
class CSharpNamespaceDetector extends PackageDetector { | ||
private static final Pattern NAMESPACE_PATTERN = Pattern.compile("^\\s*namespace\\s+([^{]*)\\s*\\{?\\s*$"); | ||
|
||
CSharpNamespaceDetector(final FileSystemFacade fileSystem) { | ||
super(fileSystem); | ||
} | ||
|
||
@Override | ||
public boolean accepts(final String fileName) { | ||
return fileName.endsWith(".cs"); | ||
} | ||
|
||
@Override | ||
Pattern getPattern() { | ||
return NAMESPACE_PATTERN; | ||
} | ||
} | ||
|
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,27 @@ | ||
package edu.hm.hafner.util; | ||
|
||
import java.util.regex.Pattern; | ||
|
||
/** | ||
* Detects the package name of a Java file. | ||
* | ||
* @author Ullrich Hafner | ||
*/ | ||
class JavaPackageDetector extends PackageDetector { | ||
private static final Pattern PACKAGE_PATTERN = Pattern.compile( | ||
"^\\s*package\\s*([a-z]+[.\\w]*)\\s*;.*"); | ||
|
||
JavaPackageDetector(final FileSystemFacade fileSystem) { | ||
super(fileSystem); | ||
} | ||
|
||
@Override | ||
Pattern getPattern() { | ||
return PACKAGE_PATTERN; | ||
} | ||
|
||
@Override | ||
public boolean accepts(final String fileName) { | ||
return fileName.endsWith(".java"); | ||
} | ||
} |
28 changes: 28 additions & 0 deletions
28
src/main/java/edu/hm/hafner/util/KotlinPackageDetector.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,28 @@ | ||
package edu.hm.hafner.util; | ||
|
||
import java.util.regex.Pattern; | ||
|
||
/** | ||
* Detects the package name of a Kotlin file. | ||
* | ||
* @author Bastian Kersting | ||
*/ | ||
class KotlinPackageDetector extends PackageDetector { | ||
private static final Pattern PACKAGE_PATTERN = Pattern.compile( | ||
"^\\s*package\\s*([a-z]+[.\\w]*)\\s*.*"); | ||
|
||
@VisibleForTesting | ||
KotlinPackageDetector(final FileSystemFacade fileSystem) { | ||
super(fileSystem); | ||
} | ||
|
||
@Override | ||
Pattern getPattern() { | ||
return PACKAGE_PATTERN; | ||
} | ||
|
||
@Override | ||
boolean accepts(final String fileName) { | ||
return fileName.endsWith(".kt"); | ||
} | ||
} |
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,110 @@ | ||
package edu.hm.hafner.util; | ||
|
||
import java.io.BufferedReader; | ||
import java.io.IOException; | ||
import java.io.InputStream; | ||
import java.io.InputStreamReader; | ||
import java.nio.charset.Charset; | ||
import java.nio.file.Files; | ||
import java.nio.file.InvalidPathException; | ||
import java.nio.file.Paths; | ||
import java.util.Optional; | ||
import java.util.regex.Matcher; | ||
import java.util.regex.Pattern; | ||
import java.util.stream.Stream; | ||
|
||
import org.apache.commons.io.input.BOMInputStream; | ||
|
||
import com.google.errorprone.annotations.MustBeClosed; | ||
|
||
/** | ||
* Base class for package detectors. | ||
* | ||
* @author Ullrich Hafner | ||
*/ | ||
abstract class PackageDetector { | ||
private final FileSystemFacade fileSystem; | ||
|
||
/** | ||
* Creates a new instance of {@link PackageDetector}. | ||
* | ||
* @param fileSystem | ||
* file system facade | ||
*/ | ||
PackageDetector(final FileSystemFacade fileSystem) { | ||
this.fileSystem = fileSystem; | ||
} | ||
|
||
/** | ||
* Detects the package or namespace name of the specified file. | ||
* | ||
* @param fileName | ||
* the file name of the file to scan | ||
* @param charset | ||
* the charset to use when reading the source files | ||
* | ||
* @return the detected package or namespace name | ||
*/ | ||
public Optional<String> detectPackageName(final String fileName, final Charset charset) { | ||
try (var stream = fileSystem.openFile(fileName)) { | ||
return detectPackageName(stream, charset); | ||
} | ||
catch (IOException | InvalidPathException ignore) { | ||
// ignore IO errors | ||
} | ||
return Optional.empty(); | ||
} | ||
|
||
private Optional<String> detectPackageName(final InputStream stream, final Charset charset) throws IOException { | ||
try (var buffer = new BufferedReader( | ||
new InputStreamReader(BOMInputStream.builder().setInputStream(stream).get(), charset))) { | ||
return detectPackageName(buffer.lines()); | ||
} | ||
} | ||
|
||
/** | ||
* Detects the package or namespace name of the specified input stream. The stream will be closed automatically by | ||
* the caller of this method. | ||
* | ||
* @param lines | ||
* the content of the file to scan | ||
* | ||
* @return the detected package or namespace name | ||
*/ | ||
private Optional<String> detectPackageName(final Stream<String> lines) { | ||
Pattern pattern = getPattern(); | ||
return lines.map(pattern::matcher) | ||
.filter(Matcher::matches) | ||
.findFirst() | ||
.map(matcher -> matcher.group(1)) | ||
.map(String::trim); | ||
} | ||
|
||
/** | ||
* Returns the Pattern for the Package Name in this kind of file. | ||
* | ||
* @return the Pattern. | ||
*/ | ||
abstract Pattern getPattern(); | ||
|
||
/** | ||
* Returns whether this classifier accepts the specified file for processing. | ||
* | ||
* @param fileName | ||
* the file name | ||
* | ||
* @return {@code true} if the classifier accepts the specified file for processing. | ||
*/ | ||
abstract boolean accepts(String fileName); | ||
|
||
/** | ||
* Facade for file system operations. May be replaced by stubs in test cases. | ||
*/ | ||
@VisibleForTesting | ||
static class FileSystemFacade { | ||
@MustBeClosed | ||
InputStream openFile(final String fileName) throws IOException, InvalidPathException { | ||
return Files.newInputStream(Paths.get(fileName)); | ||
} | ||
} | ||
} |
31 changes: 31 additions & 0 deletions
31
src/main/java/edu/hm/hafner/util/PackageDetectorFactory.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,31 @@ | ||
package edu.hm.hafner.util; | ||
|
||
import edu.hm.hafner.util.PackageDetector.FileSystemFacade; | ||
|
||
/** | ||
* Factory to create package detectors. | ||
* | ||
* @author Ullrich Hafner | ||
*/ | ||
public final class PackageDetectorFactory { | ||
/** | ||
* Creates a new package detector runner that uses the detectors for Java, Kotlin, and C#. | ||
* | ||
* @return the package detector runner | ||
*/ | ||
public static PackageDetectorRunner createPackageDetectors() { | ||
return createPackageDetectors(new FileSystemFacade()); | ||
} | ||
|
||
@VisibleForTesting | ||
static PackageDetectorRunner createPackageDetectors(final FileSystemFacade facade) { | ||
return new PackageDetectorRunner( | ||
new JavaPackageDetector(facade), | ||
new KotlinPackageDetector(facade), | ||
new CSharpNamespaceDetector(facade)); | ||
} | ||
|
||
private PackageDetectorFactory() { | ||
// prevents instantiation | ||
} | ||
} |
37 changes: 37 additions & 0 deletions
37
src/main/java/edu/hm/hafner/util/PackageDetectorRunner.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,37 @@ | ||
package edu.hm.hafner.util; | ||
|
||
import java.nio.charset.Charset; | ||
import java.util.Arrays; | ||
import java.util.List; | ||
import java.util.Optional; | ||
|
||
/** | ||
* Detects package or namespace names of files in the file system. | ||
* | ||
* @author Ullrich Hafner | ||
*/ | ||
public class PackageDetectorRunner { | ||
private final List<PackageDetector> detectors; | ||
|
||
PackageDetectorRunner(final PackageDetector... detectors) { | ||
this.detectors = Arrays.asList(detectors); | ||
} | ||
|
||
/** | ||
* Detects the package name of the specified file based on several detector strategies. | ||
* | ||
* @param fileName | ||
* the filename of the file to scan | ||
* @param charset | ||
* the charset to use when reading the source files | ||
* | ||
* @return the detected package name or {@link Optional#empty()} if no package name could be detected | ||
*/ | ||
public Optional<String> detectPackageName(final String fileName, final Charset charset) { | ||
return detectors.stream() | ||
.filter(detector -> detector.accepts(fileName)) | ||
.map(detector -> detector.detectPackageName(fileName, charset)) | ||
.flatMap(Optional::stream) | ||
.findFirst(); | ||
} | ||
} |
59 changes: 59 additions & 0 deletions
59
src/test/java/edu/hm/hafner/util/PackageDetectorRunnerTest.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,59 @@ | ||
package edu.hm.hafner.util; | ||
|
||
import java.io.IOException; | ||
import java.io.InputStream; | ||
import java.nio.charset.StandardCharsets; | ||
import java.util.Optional; | ||
|
||
import org.junit.jupiter.api.Test; | ||
import org.junit.jupiter.params.ParameterizedTest; | ||
import org.junit.jupiter.params.provider.CsvSource; | ||
import org.junit.jupiter.params.provider.ValueSource; | ||
|
||
import edu.hm.hafner.util.PackageDetector.FileSystemFacade; | ||
|
||
import static org.assertj.core.api.Assertions.*; | ||
import static org.mockito.Mockito.*; | ||
|
||
/** | ||
* Tests the class {@link PackageDetectorRunner}. | ||
* | ||
* @author Ullrich Hafner | ||
*/ | ||
class PackageDetectorRunnerTest extends ResourceTest { | ||
@ParameterizedTest(name = "{index} => file={0}, expected package={1}") | ||
@CsvSource({ | ||
"MavenJavaTest.txt.java, hudson.plugins.tasks.util", | ||
"ActionBinding.cs, Avaloq.SmartClient.Utilities", | ||
"KotlinTest.txt.kt, edu.hm.kersting", | ||
}) | ||
void shouldExtractPackageNames(final String fileName, final String expectedPackage) throws IOException { | ||
assertThat(detect(fileName)).contains(expectedPackage); | ||
} | ||
|
||
@ParameterizedTest(name = "{index} => file={0}, no package found") | ||
@ValueSource(strings = {"MavenJavaTest.txt", "empty.txt", "KotlinTest.txt"}) | ||
void shouldNotAcceptFile(final String fileName) throws IOException { | ||
assertThat(detect(fileName)).isEmpty(); | ||
} | ||
|
||
private Optional<String> detect(final String fileName) throws IOException { | ||
try (InputStream stream = asInputStream(fileName)) { | ||
var fileSystem = mock(FileSystemFacade.class); | ||
when(fileSystem.openFile(fileName)).thenReturn(stream); | ||
|
||
var detectors = PackageDetectorFactory.createPackageDetectors(fileSystem); | ||
|
||
return detectors.detectPackageName(fileName, StandardCharsets.UTF_8); | ||
} | ||
} | ||
|
||
@Test | ||
void shouldHandleException() throws IOException { | ||
var fileSystem = mock(FileSystemFacade.class); | ||
when(fileSystem.openFile(anyString())).thenThrow(new IOException("Simulated")); | ||
|
||
assertThat(PackageDetectorFactory.createPackageDetectors(fileSystem) | ||
.detectPackageName("file.java", StandardCharsets.UTF_8)).isEmpty(); | ||
} | ||
} |
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,18 @@ | ||
/* | ||
* Author: Hafner Ullrich | ||
*/ | ||
|
||
using System; | ||
using System.ComponentModel; | ||
using System.Diagnostics.CodeAnalysis; | ||
using System.Windows.Forms; | ||
using Avaloq.Utilities; | ||
using log4net; | ||
|
||
namespace Avaloq.SmartClient.Utilities { | ||
/// <summary> | ||
/// Acts as mediator between an <see cref="Action"/> and a clickable Windows Forms control. | ||
/// </summary> | ||
public class ActionBinding : IDisposable | ||
// Rest of the file is omitted | ||
} |
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,5 @@ | ||
package edu.hm.kersting | ||
|
||
class HelloWorld { | ||
fun main() = println("Hello World") | ||
} |
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,5 @@ | ||
package edu.hm.kersting | ||
|
||
class HelloWorld { | ||
fun main() = println("Hello World") | ||
} |
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,6 @@ | ||
package hudson.plugins.tasks.util; | ||
|
||
/** | ||
* Indicates an orderly abortion of the processing. | ||
* | ||
// Rest of the file is omitted |
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,6 @@ | ||
package hudson.plugins.tasks.util; | ||
|
||
/** | ||
* Indicates an orderly abortion of the processing. | ||
*/ | ||
// Rest of the file is omitted |
File renamed without changes.