diff --git a/README.md b/README.md index af0466b..c1748e4 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ To use the plugin, insert the following configuration into the plugins section o org.atp-fivt export-maven-plugin - 1.1 + 1.2 Name_Surname.zip diff --git a/pom.xml b/pom.xml index 55c66a8..870aeaa 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.atp-fivt export-maven-plugin - 1.1 + 1.2 maven-plugin Maven project export plugin @@ -48,13 +48,6 @@ 10.25.0 - - - ossrh - https://oss.sonatype.org/service/local/staging/deploy/maven2/ - - - org.eclipse.jgit @@ -156,6 +149,45 @@ + + org.apache.maven.plugins + maven-source-plugin + 3.3.1 + + + attach-sources + + prepare-package + + + jar-no-fork + + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 3.6.3 + + + ${maven.compiler.target} + none + false + + + + attach-javadoc + prepare-package + + jar + + + + + + org.apache.maven.plugins maven-gpg-plugin @@ -171,14 +203,14 @@ - org.sonatype.plugins - nexus-staging-maven-plugin - 1.7.0 + org.sonatype.central + central-publishing-maven-plugin + 0.7.0 true - ossrh - https://oss.sonatype.org/ - true + central + true + validated diff --git a/src/main/java/org/atpfivt/ExportMojo.java b/src/main/java/org/atpfivt/ExportMojo.java index 00d7bb2..0e7fb96 100644 --- a/src/main/java/org/atpfivt/ExportMojo.java +++ b/src/main/java/org/atpfivt/ExportMojo.java @@ -11,6 +11,7 @@ import java.io.FileOutputStream; import java.io.IOException; import java.net.URI; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.util.List; @@ -18,6 +19,8 @@ import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; +import static org.atpfivt.StripMarkedSections.stripMarkedSections; + /** * Goal which exports the project into zip file respecting .gitignore. */ @@ -33,6 +36,9 @@ public final class ExportMojo extends AbstractMojo { @Parameter(defaultValue = "export.zip") private String zipFileName; + @Parameter(defaultValue = "false") + private boolean stripMarked; + void setBaseDirectory(File baseDirectory) { this.baseDirectory = baseDirectory; } @@ -45,10 +51,15 @@ void setZipFileName(String zipFileName) { this.zipFileName = zipFileName; } + void setStripMarked(boolean stripMarked) { + this.stripMarked = stripMarked; + } + private boolean isNotInBuildDirectory(Path path) { return !path.startsWith(buildDirectory.toPath()); } + public void execute() throws MojoExecutionException { Path buildPath = buildDirectory.toPath(); @@ -65,7 +76,26 @@ public void execute() for (Path path : pathList) { URI relativePath = basePath.toUri().relativize(path.toUri()); zos.putNextEntry(new ZipEntry(relativePath.toString())); - Files.copy(path, zos); + String fileName = path.getFileName().toString(); + if (stripMarked && fileName.endsWith(".java")) { + /* ----- JAVA SOURCE: strip //[[ … //]] blocks ----- */ + String src = Files.readString(path, StandardCharsets.UTF_8); + String cleaned = stripMarkedSections(src); + if (cleaned.length() != src.length()) { + getLog().info("Stripped file: " + path); + } + zos.write(cleaned.getBytes(StandardCharsets.UTF_8)); + + } else if (stripMarked && "pom.xml".equals(fileName)) { + /* ----- POM: remove the export-plugin marker ----- */ + String pom = Files.readString(path, StandardCharsets.UTF_8); + String cleaned = pom.replace("true", ""); + zos.write(cleaned.getBytes(StandardCharsets.UTF_8)); + + } else { + /* ----- Everything else copied verbatim ----- */ + Files.copy(path, zos); + } zos.closeEntry(); } } diff --git a/src/main/java/org/atpfivt/StripMarkedSections.java b/src/main/java/org/atpfivt/StripMarkedSections.java new file mode 100644 index 0000000..92489f6 --- /dev/null +++ b/src/main/java/org/atpfivt/StripMarkedSections.java @@ -0,0 +1,27 @@ +package org.atpfivt; + +final class StripMarkedSections { + private StripMarkedSections() { + + } + static String stripMarkedSections(String src) { + StringBuilder out = new StringBuilder(src.length()); + int cursor = 0; + + while (cursor < src.length()) { + int open = src.indexOf("//[[", cursor); + if (open == -1) { // no more markers + out.append(src, cursor, src.length()); + break; + } + out.append(src, cursor, open); // keep text before "[[" + int close = src.indexOf("//]]", open + 4); + if (close == -1) { // unmatched start → keep the rest + out.append(src, open, src.length()); + break; + } + cursor = close + 4; // jump past "]]" and continue + } + return out.toString(); + } +} diff --git a/src/test/java/org/atpfivt/ExportMojoTest.java b/src/test/java/org/atpfivt/ExportMojoTest.java index 683e4e1..49d7d01 100644 --- a/src/test/java/org/atpfivt/ExportMojoTest.java +++ b/src/test/java/org/atpfivt/ExportMojoTest.java @@ -5,19 +5,18 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; -import java.util.Comparator; -import java.util.HashSet; -import java.util.List; -import java.util.Set; +import java.util.*; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; -import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.*; class ExportMojoTest { private ExportMojo exportMojo; @@ -50,6 +49,10 @@ void exportSavesFilesRespectingTheGitignore() throws MojoExecutionException, IOE ZipEntry nextEntry; while ((nextEntry = zipInputStream.getNextEntry()) != null) { filenames.add(nextEntry.getName()); + try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { + zipInputStream.transferTo(baos); + assertTrue(baos.toString(StandardCharsets.UTF_8).contains("test")); + } } } assertEquals(Set.of("a", "b", ".gitignore", "src/foo"), filenames); @@ -67,11 +70,83 @@ void exportOmitsTargetEvenIfNoGitignoreProvided() throws MojoExecutionException, ZipEntry nextEntry; while ((nextEntry = zipInputStream.getNextEntry()) != null) { filenames.add(nextEntry.getName()); + try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { + zipInputStream.transferTo(baos); + assertEquals("test", baos.toString(StandardCharsets.UTF_8)); + } } } assertEquals(Set.of("a", "b", "a.ignoreme"), filenames); } + + @Test + void exportStripsMarkedSectionsAndPomTag() throws MojoExecutionException, IOException { + /* ── Arrange: project structure ─────────────────────────────────────────── */ + // Java source with two marked regions + String javaSrc = + "public class StudentExercise {\n" + + " //[[\n" + + " private int answer = 42;\n" + + " //]]\n" + + " public void solve() {\n" + + " //[[\n" + + " System.out.println(answer);\n" + + " //]]\n" + + " }\n" + + "}\n"; + Files.writeString(root.resolve("StudentExercise.java"), javaSrc, StandardCharsets.UTF_8); + + // POM with the marker flag + String pom = + "\n" + + " 4.0.0\n" + + " example\n" + + " exercise\n" + + " 1.0-SNAPSHOT\n" + + " \n" + + " true\n" + + " \n" + + "\n"; + Files.writeString(root.resolve("pom.xml"), pom, StandardCharsets.UTF_8); + + // Enable strip-marked mode + exportMojo.setStripMarked(true); + + /* ── Act: run the mojo ──────────────────────────────────────────────────── */ + exportMojo.execute(); + + /* ── Assert: check the produced ZIP ─────────────────────────────────────── */ + Map zipContents = new HashMap<>(); + try (ZipInputStream zis = new ZipInputStream( + new FileInputStream(target.resolve("export.zip").toFile()), + StandardCharsets.UTF_8)) { + + ZipEntry entry; + while ((entry = zis.getNextEntry()) != null) { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + zis.transferTo(baos); + zipContents.put(entry.getName(), baos.toString(StandardCharsets.UTF_8)); + } + } + + // 1. ZIP contains the expected files + assertEquals(Set.of("StudentExercise.java", "pom.xml"), zipContents.keySet()); + + // 2. Java source has no \\[[ or \\]] and the secret implementation vanished + String strippedJava = zipContents.get("StudentExercise.java"); + assertFalse(strippedJava.contains("//[["), "Markers must be removed"); + assertFalse(strippedJava.contains("//]]"), "Markers must be removed"); + assertFalse(strippedJava.contains("answer = 42"), "Hidden code must be removed"); + assertFalse(strippedJava.contains("println"), "Second hidden block must be removed"); + + // 3. pom.xml no longer contains the marker element + String strippedPom = zipContents.get("pom.xml"); + assertFalse(strippedPom.contains("true"), + "pom.xml should not expose the stripMarked flag to students"); + } + + @AfterEach void tearDown() throws IOException { Files.walk(root) diff --git a/src/test/java/org/atpfivt/StripMarkedSectionsTest.java b/src/test/java/org/atpfivt/StripMarkedSectionsTest.java new file mode 100644 index 0000000..6e96b60 --- /dev/null +++ b/src/test/java/org/atpfivt/StripMarkedSectionsTest.java @@ -0,0 +1,35 @@ +package org.atpfivt; + +import org.junit.jupiter.api.Test; + +import static org.atpfivt.StripMarkedSections.stripMarkedSections; +import static org.junit.jupiter.api.Assertions.*; + +class StripMarkedSectionsTest { + + @Test + void noMarkers() { + String src = "public class Foo { }"; + assertEquals(src, stripMarkedSections(src)); + } + + @Test + void oneMarker() { + String src = "before //[[hidden//]] after"; + String expected = "before after"; + assertEquals(expected, stripMarkedSections(src)); + } + + @Test + void multipleMarkers() { + String src = "a //[[x//]] b //[[y//]] c"; + String expected = "a b c"; + assertEquals(expected, stripMarkedSections(src)); + } + + @Test + void unmatchedStart() { + String src = "keep //[[oops"; + assertEquals(src, stripMarkedSections(src)); + } +} \ No newline at end of file