package hudson.plugins.emailext.plugins.content; import hudson.model.AbstractBuild; import hudson.model.AbstractProject; import hudson.model.Result; import hudson.plugins.emailext.EmailType; import hudson.plugins.emailext.ExtendedEmailPublisher; import hudson.plugins.emailext.Util; import hudson.plugins.emailext.Util.PrintfSpec; import hudson.plugins.emailext.plugins.EmailContent; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; public class ChangesSinceLastSuccessfulBuildContent implements EmailContent { private static final String TOKEN = "CHANGES_SINCE_LAST_SUCCESS"; private static final String REVERSE_ARG_NAME = "reverse"; private static final boolean REVERSE_DEFAULT_VALUE = false; private static final String FORMAT_ARG_NAME = "format"; private static final String FORMAT_DEFAULT_VALUE = "Changes for Build #%n\\n%c\\n"; private static final String SHOW_PATHS_ARG_NAME = "showPaths"; private static final String CHANGES_FORMAT_ARG_NAME = "changesFormat"; private static final String PATH_FORMAT_ARG_NAME = "pathFormat"; public String getToken() { return TOKEN; } public List<String> getArguments() { return Arrays.asList(REVERSE_ARG_NAME, FORMAT_ARG_NAME, SHOW_PATHS_ARG_NAME, CHANGES_FORMAT_ARG_NAME, PATH_FORMAT_ARG_NAME); } public String getHelpText() { return "Displays the changes since the last successful build.\n" + "<ul>\n" + "<li><i>" + REVERSE_ARG_NAME + "</i> - indicates that " + "most recent builds should be at the top.<br>\n" + "Defaults to " + REVERSE_DEFAULT_VALUE + ".\n" + "<li><i>" + FORMAT_ARG_NAME + "</i> - for each build listed, " + "a string containing %X, where %X is one of %c for changes, " + "or %n for build number.<br>\n" + "Defaults to \"" + FORMAT_DEFAULT_VALUE + "\".\n" + "<li><i>" + SHOW_PATHS_ARG_NAME + "</i>, <i>" + CHANGES_FORMAT_ARG_NAME + "</i>, <i>" + PATH_FORMAT_ARG_NAME + "</i> - " + "defined as <i>showPaths</i>, <i>format</i>, and <i>pathFormat</i> " + "from ${CHANGES}, repectively.\n" + "</ul>\n"; } public <P extends AbstractProject<P, B>, B extends AbstractBuild<P, B>> String getContent(AbstractBuild<P, B> build, ExtendedEmailPublisher publisher, EmailType emailType, Map<String, ?> args) { if (build.getPreviousBuild() == null) { return ""; } AbstractBuild<P,B> firstIncludedBuild = build; { B prev = firstIncludedBuild.getPreviousBuild(); while (prev != null && prev.getResult() != Result.SUCCESS) { firstIncludedBuild = prev; prev = firstIncludedBuild.getPreviousBuild(); } } String formatString = Args.get(args, FORMAT_ARG_NAME, FORMAT_DEFAULT_VALUE); boolean reverseOrder = Args.get(args, REVERSE_ARG_NAME, REVERSE_DEFAULT_VALUE); Map<String, Object> childArgs = new HashMap<String, Object>(); childArgs.put(FORMAT_ARG_NAME, args.get(CHANGES_FORMAT_ARG_NAME)); childArgs.put(PATH_FORMAT_ARG_NAME, args.get(PATH_FORMAT_ARG_NAME)); StringBuffer sb = new StringBuffer(); final AbstractBuild<P, B> startBuild; final AbstractBuild<P, B> endBuild; if (reverseOrder) { startBuild = build; endBuild = firstIncludedBuild; } else { startBuild = firstIncludedBuild; endBuild = build; } AbstractBuild<P,B> currentBuild = null; while(currentBuild != endBuild) { if (currentBuild == null) { currentBuild = startBuild; } else { if (reverseOrder) { currentBuild = currentBuild.getPreviousBuild(); } else { currentBuild = currentBuild.getNextBuild(); } } appendBuild(sb, formatString, publisher, emailType, currentBuild, childArgs); } return sb.toString(); } private <P extends AbstractProject<P, B>, B extends AbstractBuild<P, B>> void appendBuild(StringBuffer buf, String formatString, final ExtendedEmailPublisher publisher, final EmailType emailType, final AbstractBuild<P, B> currentBuild, final Map<String, Object> childArgs) { // Use this object since it already formats the changes per build final ChangesSinceLastBuildContent changes = new ChangesSinceLastBuildContent(); Util.printf(buf, formatString, new PrintfSpec() { public boolean printSpec(StringBuffer buf, char formatChar) { switch (formatChar) { case 'c': buf.append(changes.getContent(currentBuild, publisher, emailType, childArgs)); return true; case 'n': buf.append(currentBuild.getNumber()); return true; default: return false; } } }); } public boolean hasNestedContent() { return false; } }