Skip to content

Commit

Permalink
refactor: sonarcloud quality gate (#111)
Browse files Browse the repository at this point in the history
* refactor(S1118): utility classes should not have public constructors

* refactor: fail-fast approach while splitting

* refactor: adopt java 17 features

* refactor: drop unreachable statement

* refactor: generic data factory provider

* fix: invalid mockito-inline version

* refactor: rename test data factory

* feat: switch to mixed memory buffer

* refactor: command handler + test coverage

* test: make path tests os-agnostic

* fix: list files sorted by name

* test: cover command factory

* test: fix new line test output

* test: cover no-arg command execution
  • Loading branch information
ParanoidUser authored Jul 5, 2023
1 parent d96a07e commit d97708e
Show file tree
Hide file tree
Showing 36 changed files with 730 additions and 120 deletions.
8 changes: 8 additions & 0 deletions opdf-api/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,12 @@

<artifactId>opdf-api</artifactId>
<name>Open PDF Tool :: API</name>

<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,15 @@

public final class DataFactoryProvider {

public static DataMergerFactory getMergerFactory() {
return getDataFactory(DataMergerFactory.class);
}

public static DataSplitterFactory getSplitterFactory() {
return getDataFactory(DataSplitterFactory.class);
}

private static <T> T getDataFactory(Class<T> type) {
public static <T> T getInstance(Class<T> type) {
Iterator<T> iterator = ServiceLoader.load(type).iterator();
if (!iterator.hasNext()) {
throw new NoClassDefFoundError("Cannot find any implementations of factory class: " + type);
}
return iterator.next();
}

private DataFactoryProvider() {
throw new UnsupportedOperationException();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package dev.noid.toolbox.opdf.spi;

import static dev.noid.toolbox.opdf.spi.DataFactoryProvider.getInstance;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;

import java.lang.reflect.Constructor;
import org.junit.jupiter.api.Test;

class DataFactoryProviderTest {

@Test
void lookup_default_merger_factory() {
assertEquals(NoopDataFactory.class, getInstance(DataMergerFactory.class).getClass());
}

@Test
void lookup_default_splitter_factory() {
assertEquals(NoopDataFactory.class, getInstance(DataSplitterFactory.class).getClass());
}

@Test
void lookup_unknown_data_factory() {
var error = assertThrows(NoClassDefFoundError.class, () -> getInstance(UnknownDataFactory.class));
assertEquals("Cannot find any implementations of factory class: " + UnknownDataFactory.class, error.getMessage());
}

@Test
void prohibit_instance_creation() {
try {
Constructor<DataFactoryProvider> constructor = DataFactoryProvider.class.getDeclaredConstructor();
constructor.setAccessible(true);
constructor.newInstance();
} catch (Exception cause) {
assertEquals(UnsupportedOperationException.class, cause.getCause().getClass());
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package dev.noid.toolbox.opdf.spi;

import dev.noid.toolbox.opdf.api.DataMerger;
import dev.noid.toolbox.opdf.api.DataSplitter;

public class NoopDataFactory implements DataMergerFactory, DataSplitterFactory {

@Override
public DataMerger getMerger() {
return (sources, sink) -> {};
}

@Override
public DataSplitter getSplitter() {
return (source, sink) -> {};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package dev.noid.toolbox.opdf.spi;

public interface UnknownDataFactory {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
dev.noid.toolbox.opdf.spi.NoopDataFactory
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
dev.noid.toolbox.opdf.spi.NoopDataFactory
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package dev.noid.toolbox.opdf.cli;

import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Function;

class CommandFactory {

private final Map<String, Function<String[], Runnable>> registry;

CommandFactory(Consumer<String> stdout) {
this.registry = Map.of(
"help", args -> new HelpCommand(stdout),
"version", args -> new VersionCommand(stdout),
"split", args -> new SplitCommand(getSource(args)),
"merge", args -> new MergeCommand(getSource(args))
);
}

public Runnable getCommand(String... args) {
String key = args.length > 0 ? args[0] : "help";
return registry.getOrDefault(key, registry.get("help")).apply(args);
}

private Path getSource(String[] args) {
if (args.length < 2) {
throw new IllegalArgumentException("Source path is missing");
}
return Paths.get(args[1]);
}
}
28 changes: 28 additions & 0 deletions opdf-cli/src/main/java/dev/noid/toolbox/opdf/cli/HelpCommand.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package dev.noid.toolbox.opdf.cli;

import java.util.function.Consumer;

class HelpCommand implements Runnable {

private static final String HELP = """
Open PDF Tool
Usage: opdf [COMMAND]
Commands:
help Displays help information about the specified command
version Displays version information
merge Merge several PDF files into one
split Split one PDF file into pages
""";

private final Consumer<String> stdout;

HelpCommand(Consumer<String> stdout) {
this.stdout = stdout;
}

@Override
public void run() {
stdout.accept(HELP);
}
}
36 changes: 36 additions & 0 deletions opdf-cli/src/main/java/dev/noid/toolbox/opdf/cli/MergeCommand.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package dev.noid.toolbox.opdf.cli;

import dev.noid.toolbox.opdf.api.DataMerger;
import dev.noid.toolbox.opdf.api.DataSink;
import dev.noid.toolbox.opdf.io.FileSink;
import dev.noid.toolbox.opdf.io.FileSource;
import dev.noid.toolbox.opdf.spi.DataFactoryProvider;
import dev.noid.toolbox.opdf.spi.DataMergerFactory;

import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.stream.Stream;

class MergeCommand implements Runnable {

private final Path sourceDir;

MergeCommand(Path sourceDir) {
this.sourceDir = sourceDir;
}

@Override
public void run() {
Path outputFile = sourceDir.resolve("merged.pdf");
try (Stream<Path> fileStream = Files.list(sourceDir).sorted()) {
List<FileSource> sources = fileStream.map(FileSource::of).toList();
DataSink sink = FileSink.of(outputFile);

DataMerger merger = DataFactoryProvider.getInstance(DataMergerFactory.class).getMerger();
merger.merge(sources, sink);
} catch (Exception cause) {
throw new IllegalArgumentException(cause);
}
}
}
79 changes: 1 addition & 78 deletions opdf-cli/src/main/java/dev/noid/toolbox/opdf/cli/Opdf.java
Original file line number Diff line number Diff line change
@@ -1,85 +1,8 @@
package dev.noid.toolbox.opdf.cli;

import dev.noid.toolbox.opdf.api.DataMerger;
import dev.noid.toolbox.opdf.api.DataSink;
import dev.noid.toolbox.opdf.api.DataSource;
import dev.noid.toolbox.opdf.api.DataSplitter;
import dev.noid.toolbox.opdf.io.FileSink;
import dev.noid.toolbox.opdf.io.FileSource;
import dev.noid.toolbox.opdf.io.FileUtil;
import dev.noid.toolbox.opdf.io.NamingStrategy;
import dev.noid.toolbox.opdf.spi.DataFactoryProvider;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class Opdf {

public static void main(String[] args) {
if (args.length == 0) {
printHelp();
} else if ("version".equals(args[0])) {
printVersion();
} else if ("split".equals(args[0])) {
splitDocument(Paths.get(args[1]));
} else if ("merge".equals(args[0])) {
mergeDocuments(Paths.get(args[1]));
} else {
printHelp();
}
}

private static void printHelp() {
String help = "Open PDF Tool\n"
+ "\n"
+ "Usage: opdf [COMMAND]\n"
+ "Commands:\n"
+ " help Displays help information about the specified command\n"
+ " version Displays version information\n"
+ " merge Merge several PDF files into one\n"
+ " split Split one PDF file into pages";
System.out.println(help);
}

private static void printVersion() {
String version = Opdf.class.getPackage().getImplementationVersion();
System.out.println(version);
}

private static void splitDocument(Path sourceFile) {
Path outputDirectory = sourceFile.getParent();
DataSource source = new FileSource(sourceFile);
DataSink sink = getStreamSink(outputDirectory, sourceFile);
DataSplitter splitter = DataFactoryProvider.getSplitterFactory().getSplitter();
splitter.split(source, sink);
}

private static void mergeDocuments(Path sourceDir) {
if (!Files.isDirectory(sourceDir)) {
return;
}

Path outputFile = sourceDir.resolve("merged.pdf");
try (Stream<Path> fileStream = Files.list(sourceDir)) {
List<DataSource> sources = fileStream.map(FileSource::new).collect(Collectors.toList());
DataSink sink = new FileSink(outputFile);

DataMerger merger = DataFactoryProvider.getMergerFactory().getMerger();
merger.merge(sources, sink);
} catch (Exception cause) {
throw new IllegalArgumentException(cause);
}
}

private static DataSink getStreamSink(Path targetDir, Path sourceFile) {
String nameOnly = FileUtil.getNameWithoutExtension(sourceFile);
String filePath = targetDir.resolve(nameOnly).toString();
String extension = FileUtil.getExtension(sourceFile);

NamingStrategy naming = NamingStrategy.ordinary(filePath, extension);
return () -> new FileSink(Paths.get(naming.getName())).getWriting();
new CommandFactory(System.out::println).getCommand(args).run();
}
}
41 changes: 41 additions & 0 deletions opdf-cli/src/main/java/dev/noid/toolbox/opdf/cli/SplitCommand.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package dev.noid.toolbox.opdf.cli;

import dev.noid.toolbox.opdf.api.DataSink;
import dev.noid.toolbox.opdf.api.DataSource;
import dev.noid.toolbox.opdf.api.DataSplitter;
import dev.noid.toolbox.opdf.io.FileSink;
import dev.noid.toolbox.opdf.io.FileSource;
import dev.noid.toolbox.opdf.io.FileUtil;
import dev.noid.toolbox.opdf.io.NamingStrategy;
import dev.noid.toolbox.opdf.spi.DataFactoryProvider;
import dev.noid.toolbox.opdf.spi.DataSplitterFactory;

import java.nio.file.Path;
import java.nio.file.Paths;

class SplitCommand implements Runnable {

private final Path sourceFile;

SplitCommand(Path sourceFile) {
this.sourceFile = sourceFile;
}

@Override
public void run() {
Path outputDirectory = sourceFile.getParent();
DataSource source = FileSource.of(sourceFile);
DataSink sink = getStreamSink(outputDirectory, sourceFile);
DataSplitter splitter = DataFactoryProvider.getInstance(DataSplitterFactory.class).getSplitter();
splitter.split(source, sink);
}

private DataSink getStreamSink(Path targetDir, Path sourceFile) {
String nameOnly = FileUtil.getNameWithoutExtension(sourceFile);
String filePath = targetDir.resolve(nameOnly).toString();
String extension = FileUtil.getExtension(sourceFile);

NamingStrategy naming = NamingStrategy.ordinary(filePath, extension);
return () -> FileSink.of(Paths.get(naming.getName())).getWriting();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package dev.noid.toolbox.opdf.cli;

import java.util.Objects;
import java.util.function.Consumer;

class VersionCommand implements Runnable {

private final Consumer<String> stdout;
private final String version;

VersionCommand(Consumer<String> stdout) {
this.stdout = stdout;

String implVersion = VersionCommand.class.getPackage().getImplementationVersion();
this.version = Objects.requireNonNullElse(implVersion, "unknown");
}

@Override
public void run() {
stdout.accept(version);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,13 @@

public class FileSink implements DataSink {

public static FileSink of(Path filePath) {
return new FileSink(filePath);
}

private final Path filePath;

public FileSink(Path filePath) {
private FileSink(Path filePath) {
this.filePath = filePath;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,13 @@

public class FileSource implements DataSource {

public static FileSource of(Path filePath) {
return new FileSource(filePath);
}

private final Path filePath;

public FileSource(Path filePath) {
private FileSource(Path filePath) {
this.filePath = filePath;
}

Expand Down
Loading

0 comments on commit d97708e

Please sign in to comment.