Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed

- add documentation link for each rule in RULES.md file
- Changes the file post-processing process to use Java code instead of a JShell script. This reduces build times and also allows for testing the process.

### Deleted

Expand Down
1 change: 1 addition & 0 deletions maven-build/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
This directory contains processing code used during the Maven build.
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package org.greencodeinitiative.mavenbuild.ruleexporter;

import org.greencodeinitiative.mavenbuild.ruleexporter.infra.PrepareResources;

import java.nio.file.Path;
import java.util.List;
import java.util.Optional;

import static java.util.Collections.emptyList;
import static java.util.Optional.empty;
import static java.util.Optional.ofNullable;

public class Main implements Runnable {
private final List<String> args;

public Main(String[] args) {
this.args = ofNullable(args).map(List::of).orElse(emptyList());
}

public static void main(String... args) {
new Main(args).run();
}

@Override
public void run() {
Path sourceDir = argAsPath(0, "sourceDir");
Path targetDir = argAsPath(1, "targetDir");
new PrepareResources(
sourceDir,
targetDir
).run();
}

private Optional<String> optionalArg(int index) {
if (args.size() <= index) {
return empty();
}
return Optional.of(args.get(index));
}

private String arg(int index, String description) {
if (args.size() <= index) {
throw new IllegalArgumentException("argument " + (index + 1) + " is required: " + description);
}
return optionalArg(index).orElseThrow(() -> new IllegalArgumentException("argument " + (index + 1) + " is required: " + description));
}

private Path argAsPath(int index, String description) {
return Path.of(arg(index, description));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package org.greencodeinitiative.mavenbuild.ruleexporter.domain;

import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Optional;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static java.util.Optional.empty;
import static java.util.Optional.of;

public class Rule {
/**
* Resources to include
*/
private static final Pattern TARGET_RESOURCES = Pattern.compile("^.{1,1000}/(?<ruleKey>GCI\\d{1,50})/(?<language>[^/]{1,50})/GCI\\d{1,50}\\.html$");

public static Optional<Rule> createFromHtmlDescription(Path htmlDescription) {
final Matcher matcher = TARGET_RESOURCES.matcher(htmlDescription.toString().replace('\\', '/'));
if (!matcher.find()) {
return empty();
}
final String ruleKey = matcher.group("ruleKey");
final Path metadata = htmlDescription.getParent().getParent().resolve(ruleKey + ".json");
final Path specificMetadata = htmlDescription.getParent().resolve(ruleKey + ".json");

if (!Files.isRegularFile(htmlDescription) || !Files.isRegularFile(metadata)) {
return empty();
}

return of(new Rule(
matcher.group("language"),
htmlDescription,
metadata,
specificMetadata
));
}

private final String language;
private final Path htmlDescription;
private final Path metadata;
private final Path specificMetadata;

public Rule(String language, Path htmlDescription, Path metadata, Path specificMetadata) {
this.language = language;
this.htmlDescription = htmlDescription;
this.metadata = metadata;
this.specificMetadata = specificMetadata;
}

public Path getHtmlDescriptionTargetPath(Path targetDir) {
return targetDir.resolve(language).resolve(htmlDescription.getFileName());
}

public Path getMetadataTargetPath(Path targetDir) {
return targetDir.resolve(language).resolve(metadata.getFileName());
}

public String language() {
return language;
}

public Path htmlDescription() {
return htmlDescription;
}

public Path metadata() {
return metadata;
}

public Path specificMetadata() {
return specificMetadata;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package org.greencodeinitiative.mavenbuild.ruleexporter.infra;

import jakarta.json.Json;
import jakarta.json.JsonMergePatch;
import jakarta.json.JsonObject;
import jakarta.json.JsonReader;
import jakarta.json.JsonValue;
import jakarta.json.JsonWriter;
import org.greencodeinitiative.mavenbuild.ruleexporter.domain.Rule;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static java.lang.System.Logger.Level.DEBUG;

public class PrepareResources implements Runnable {
private static final System.Logger LOGGER = System.getLogger("PrepareResources");

private final Path sourceDir;
private final Path targetDir;

public PrepareResources(Path sourceDir, Path targetDir) {
this.sourceDir = sourceDir;
this.targetDir = targetDir;
}

@Override
public void run() {
getResourcesToCopy().forEach(rule -> {
mergeOrCopyJsonMetadata(rule.metadata(), rule.specificMetadata(), rule.getMetadataTargetPath(targetDir));
copyFile(rule.htmlDescription(), rule.getHtmlDescriptionTargetPath(targetDir));
});
}

private List<Rule> getResourcesToCopy() {
try (Stream<Path> stream = Files.walk(sourceDir)) {
return stream
.filter(Files::isRegularFile)
.map(Rule::createFromHtmlDescription)
.filter(Optional::isPresent)
.map(Optional::get)
.collect(Collectors.toList());
} catch (IOException e) {
throw new IllegalStateException("Unable to read file", e);
}
}

void mergeOrCopyJsonMetadata(Path source, Path merge, Path target) {
try {
Files.createDirectories(target.getParent());
} catch (IOException e) {
throw new ProcessException(e);
}
if (Files.isRegularFile(merge)) {
mergeJsonFile(source, merge, target);
} else {
copyFile(source, target);
}
}

void mergeJsonFile(Path source, Path merge, Path target) {
LOGGER.log(DEBUG, "Merge: {0} and {1} -> {2}", source, merge, target);

try (
JsonReader sourceJsonReader = Json.createReader(Files.newBufferedReader(source));
JsonReader mergeJsonReader = Json.createReader(Files.newBufferedReader(merge));
JsonWriter resultJsonWriter = Json.createWriter(Files.newBufferedWriter(target));
) {
Files.createDirectories(target.getParent());

JsonObject sourceJson = sourceJsonReader.readObject();
JsonObject mergeJson = mergeJsonReader.readObject();

JsonMergePatch mergePatch = Json.createMergePatch(mergeJson);
JsonValue result = mergePatch.apply(sourceJson);

resultJsonWriter.write(result);
} catch (IOException e) {
throw new ProcessException("cannot process source " + source, e);
}
}

void copyFile(Path source, Path target) {
try {
LOGGER.log(DEBUG, "Copy: {0} -> {1}", source, target);
Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING);
} catch (IOException e) {
throw new ProcessException("unable to copy '" + source + "' to '" + target + "'", e);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package org.greencodeinitiative.mavenbuild.ruleexporter.infra;

public class ProcessException extends RuntimeException {
public ProcessException(Throwable cause) {
super(cause);
}

public ProcessException(String message, Throwable cause) {
super(message, cause);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package org.greencodeinitiative.mavenbuild.ruleexporter;

import org.junit.jupiter.api.Test;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.catchThrowable;

class MainTest {

@Test
void shouldThrowIfThereIsNoSourceDir() {
// Given
Main main = new Main(new String[]{});

// When
Throwable thrown = catchThrowable(main::run);

// Then
assertThat(thrown)
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("argument 1 is required: sourceDir");
}

@Test
void shouldThrowIfThereIsNoTargetDir() {
// Given
Main main = new Main(new String[]{"someSourceDir"});

// When
Throwable thrown = catchThrowable(main::run);

// Then
assertThat(thrown)
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("argument 2 is required: targetDir");
}

}
Loading