All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.codemodder.CodemodReporterStrategy Maven / Gradle / Ivy

There is a newer version: 0.98.6
Show newest version
package io.codemodder;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.file.Path;
import java.util.List;
import java.util.Objects;
import java.util.stream.StreamSupport;
import org.apache.commons.io.IOUtils;
import org.jetbrains.annotations.NotNull;

/** A type responsible for reporting codemod changes. */
public interface CodemodReporterStrategy {

  String getSummary();

  String getDescription();

  String getChange(Path path, CodemodChange change);

  List getReferences();

  /**
   * A {@link CodemodReporterStrategy} that reports based on text from a predictable location on
   * classpath. This is an alternative to storing data inline to the Java source code of your {@link
   * CodeChanger}. It's easier to maintain this "data" outside of code, so we prefer a simple
   * mechanism for doing that. Both the files read are expected to be in
   *
   * 
/com/acme/MyCodemod/
* * (assuming that's the name of your codemod type.) * *

The first expected file in that directory is {@code report.json}. It contains most of the * fields we want to report: * *

{@code
   * {
   *     "summary": "A headline describing the changes provided by this codemod",
   *     "control": "A URL linking to the source of the security control added",
   *     "change": "A description of the change suitable for a particular instance of a change",
   *     "references": [
   *          "A URL for further reading",
   *          "Another URL for further reading"
   *     ]
   * }
   * }
* *

The second file is ${@code description.md}, and it provides last field needed by a {@link * CodemodReporterStrategy } is the description of the codemod itself. This is expected to be a * bigger piece of text, and thus it is stored in a separate file where it can be easily edited * with an IDE supporting markdown. * *

Thus, in a typical Java project, using this {@link CodemodReporterStrategy } would mean your * artifact would retain at least these 3 files: * *

    *
  • src/main/java/com/acme/MyCodemod.java *
  • src/main/resources/com/acme/MyCodemod/report.json *
  • src/main/resources/com/acme/MyCodemod/description.mdli> *
* * @param codemodType the {@link CodeChanger} type to load the reporting text for * @return a {@link CodemodReporterStrategy } that reports based on text from the classpath */ static CodemodReporterStrategy fromClasspath(final Class codemodType) { Objects.requireNonNull(codemodType); // create the expected paths to the files String descriptionResource = "/" + codemodType.getName().replace('.', '/') + "/description.md"; String reportJson = "/" + codemodType.getName().replace('.', '/') + "/report.json"; return getCodemodReporterStrategy(codemodType, descriptionResource, reportJson); } /** A {@link CodemodReporterStrategy} that reports based on text files in the classpath. */ static CodemodReporterStrategy fromClasspathDirectory( final Class codemodType, final String directory) { Objects.requireNonNull(codemodType); // create the expected paths to the files String descriptionResource = directory + "/description.md"; String reportJson = directory + "/report.json"; return getCodemodReporterStrategy(codemodType, descriptionResource, reportJson); } @NotNull private static CodemodReporterStrategy getCodemodReporterStrategy( final Class codemodType, final String descriptionResource, final String reportJson) { // load the reporting text ObjectMapper mapper = new ObjectMapper(); final JsonNode parent; final String description; try { var jsonResource = codemodType.getResourceAsStream(reportJson); var mdResource = codemodType.getResourceAsStream(descriptionResource); if (jsonResource == null || mdResource == null) { throw new IllegalArgumentException( "Could not find report.json or description.md for: " + codemodType); } parent = mapper.readTree(jsonResource); description = IOUtils.toString(mdResource); } catch (IOException e) { throw new UncheckedIOException(e); } // pull the right data out String summary = parent.get("summary").asText(); String change = parent.get("change").asText(); ArrayNode referencesNode = (ArrayNode) parent.get("references"); List references = StreamSupport.stream(referencesNode.spliterator(), false).map(JsonNode::asText).toList(); return new CodemodReporterStrategy() { @Override public String getSummary() { return summary; } @Override public String getDescription() { return description; } @Override public String getChange(final Path path, final CodemodChange codeChange) { return change; } @Override public List getReferences() { return references; } }; } /** * A reporter strategy that does nothing, useful for tests or CLI situations where reporting isn't * important. */ static CodemodReporterStrategy empty() { return new CodemodReporterStrategy() { @Override public String getSummary() { throw new UnsupportedOperationException(); } @Override public String getDescription() { throw new UnsupportedOperationException(); } @Override public String getChange(Path path, CodemodChange change) { throw new UnsupportedOperationException(); } @Override public List getReferences() { throw new UnsupportedOperationException(); } }; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy