Index: src/main/java/hudson/plugins/mercurial/MercurialSCM.java =================================================================== --- src/main/java/hudson/plugins/mercurial/MercurialSCM.java (revision 23087) +++ src/main/java/hudson/plugins/mercurial/MercurialSCM.java (working copy) @@ -39,8 +39,8 @@ import java.net.MalformedURLException; import java.util.Arrays; import java.util.HashSet; +import java.util.List; import java.util.Locale; -import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.CountDownLatch; @@ -75,17 +75,18 @@ * Source repository URL from which we pull. */ private final String source; - + /** - * Prefixes of files within the repository which we're dependent on. - * Storing as member variable so as to only parse the dependencies string once. + * Prefixes of files within the repository which we're dependent on. Storing + * as member variable so as to only parse the dependencies string once. */ - private transient final Set _modules = new HashSet(); + private transient Set _modules = new HashSet(); + // Same thing, but not parsed for jelly. private final String modules; - + /** - * In-repository branch to follow. Null indicates "default". + * In-repository branch to follow. Null indicates "default". */ private final String branch; @@ -94,32 +95,19 @@ private HgBrowser browser; @DataBoundConstructor - public MercurialSCM(String installation, String source, String branch, String modules, HgBrowser browser, boolean clean) { + public MercurialSCM(final String installation, final String source, + String branch, final String modules, final HgBrowser browser, + final boolean clean) { this.installation = installation; this.source = source; this.modules = Util.fixNull(modules); this.clean = clean; - // split by commas and whitespace, except "\ " - String[] r = this.modules.split("(?.hg) + * True if we want clean check out each time. This means deleting everything + * in the workspace (except .hg) */ public boolean isClean() { return clean; } - private String findHgExe(TaskListener listener) throws IOException, InterruptedException { - for (MercurialInstallation inst : MercurialInstallation.allInstallations()) { + private String findHgExe(final TaskListener listener) throws IOException, + InterruptedException { + for (final MercurialInstallation inst : MercurialInstallation + .allInstallations()) { if (inst.getName().equals(installation)) { // XXX what about forEnvironment? - return inst.forNode(Computer.currentComputer().getNode(), listener).getHome() + "/bin/hg"; + return inst.forNode(Computer.currentComputer().getNode(), + listener).getHome() + + "/bin/hg"; } } return getDescriptor().getHgExe(); } - private static final String FILES_STYLE = "changeset = 'files:{files}\\n'\n" + "file = '{file}:'"; + private static final String FILES_STYLE = "changeset = 'files:{files}\\n'\n" + + "file = '{file}:'"; @Override - public boolean pollChanges(AbstractProject project, Launcher launcher, FilePath workspace, TaskListener listener) throws IOException, InterruptedException { - PrintStream output = listener.getLogger(); + public boolean pollChanges(final AbstractProject project, + final Launcher launcher, final FilePath workspace, + final TaskListener listener) throws IOException, + InterruptedException { + final PrintStream output = listener.getLogger(); // Mercurial requires the style file to be in a file.. - Set changedFileNames = new HashSet(); - FilePath tmpFile = workspace.createTextTempFile("tmp", "style", FILES_STYLE); + final Set changedFileNames = new HashSet(); + final FilePath tmpFile = workspace.createTextTempFile("tmp", "style", + FILES_STYLE); try { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); + final ByteArrayOutputStream baos = new ByteArrayOutputStream(); // Get the list of changed files. - ArgumentListBuilder cmd = new ArgumentListBuilder(); - cmd.add(findHgExe(listener), "incoming", "--style" , tmpFile.getRemote()); + final ArgumentListBuilder cmd = new ArgumentListBuilder(); + cmd.add(findHgExe(listener), "incoming", "--style", tmpFile + .getRemote()); cmd.add("-r", getBranch()); - joinWithTimeout( - launcher.launch().cmds(cmd).stdout(new ForkOutputStream(baos, output)).pwd(workspace).start(), - /* #4528: not in JDK 5: 1, TimeUnit.HOURS*/60 * 60, TimeUnit.SECONDS, listener); + joinWithTimeout(launcher.launch().cmds(cmd).stdout( + new ForkOutputStream(baos, output)).pwd(workspace).start(), + /* #4528: not in JDK 5: 1, TimeUnit.HOURS */60 * 60, + TimeUnit.SECONDS, listener); parseIncomingOutput(baos, changedFileNames); } finally { tmpFile.delete(); @@ -212,20 +210,26 @@ } // XXX maybe useful enough to make a convenience method on Proc? - private static final ExecutorService executor = Executors.newCachedThreadPool(); - private int joinWithTimeout(final Proc proc, final long timeout, final TimeUnit unit, - final TaskListener listener) throws IOException, InterruptedException { + private static final ExecutorService executor = Executors + .newCachedThreadPool(); + + private int joinWithTimeout(final Proc proc, final long timeout, + final TimeUnit unit, final TaskListener listener) + throws IOException, InterruptedException { final CountDownLatch latch = new CountDownLatch(1); try { executor.submit(new Runnable() { public void run() { try { if (!latch.await(timeout, unit)) { - listener.error("Timeout after " + timeout + " " + - unit.toString().toLowerCase(Locale.ENGLISH)); + listener.error("Timeout after " + + timeout + + " " + + unit.toString().toLowerCase( + Locale.ENGLISH)); proc.kill(); } - } catch (Exception x) { + } catch (final Exception x) { listener.error(x.toString()); } } @@ -237,18 +241,14 @@ } /** - * Filter out the given file name list by picking up changes that are in the modules we care about. + * Filter out the given file name list by picking up changes that are in the + * modules we care about. */ - private Set dependentChanges(Set changedFileNames) { - if (_modules == null) { - // Old project created before this feature was added. - return changedFileNames; - } + private Set dependentChanges(final Set changedFileNames) { - Set affecting = new HashSet(); - - for (String changedFile : changedFileNames) { - for (String dependency : _modules) { + final Set affecting = new HashSet(); + for (final String changedFile : changedFileNames) { + for (final String dependency : getModulesSet()) { if (changedFile.startsWith(dependency)) { affecting.add(changedFile); break; @@ -261,14 +261,15 @@ private static Pattern FILES_LINE = Pattern.compile("files:(.*)"); - private void parseIncomingOutput(ByteArrayOutputStream output, Set result) throws IOException { - BufferedReader in = new BufferedReader(new InputStreamReader( + private void parseIncomingOutput(final ByteArrayOutputStream output, + final Set result) throws IOException { + final BufferedReader in = new BufferedReader(new InputStreamReader( new ByteArrayInputStream(output.toByteArray()))); String line; while ((line = in.readLine()) != null) { - Matcher matcher = FILES_LINE.matcher(line); + final Matcher matcher = FILES_LINE.matcher(line); if (matcher.matches()) { - for (String s : matcher.group(1).split(":")) { + for (final String s : matcher.group(1).split(":")) { if (s.length() > 0) { result.add(s); } @@ -278,54 +279,76 @@ } @Override - public boolean checkout(AbstractBuild build, Launcher launcher, FilePath workspace, final BuildListener listener, File changelogFile) throws IOException, InterruptedException { - boolean canUpdate = workspace.act(new FileCallable() { - public Boolean invoke(File ws, VirtualChannel channel) throws IOException { - if(!HgRc.getHgRcFile(ws).exists()) + public boolean checkout(final AbstractBuild build, final Launcher launcher, + final FilePath workspace, final BuildListener listener, + final File changelogFile) throws IOException, InterruptedException { + final boolean canUpdate = workspace.act(new FileCallable() { + public Boolean invoke(final File ws, final VirtualChannel channel) + throws IOException { + if (!HgRc.getHgRcFile(ws).exists()) { return false; - HgRc hgrc = new HgRc(ws); + } + final HgRc hgrc = new HgRc(ws); return canUpdate(hgrc); } - private boolean canUpdate(HgRc ini) { - String upstream = ini.getSection("paths").get("default"); - if(upstream==null) return false; + private boolean canUpdate(final HgRc ini) { + final String upstream = ini.getSection("paths").get("default"); + if (upstream == null) { + return false; + } - if(upstream.equals(source)) return true; - if((upstream+'/').equals(source)) return true; - if (source.startsWith("file:/") && new File(upstream).toURI().toString().equals(source)) return true; - listener.error( - "Workspace reports paths.default as " + upstream + - "\nwhich looks different than " + source + - "\nso falling back to fresh clone rather than incremental update"); + if (upstream.equals(source)) { + return true; + } + if ((upstream + '/').equals(source)) { + return true; + } + if (source.startsWith("file:/") + && new File(upstream).toURI().toString().equals(source)) { + return true; + } + listener + .error("Workspace reports paths.default as " + + upstream + + "\nwhich looks different than " + + source + + "\nso falling back to fresh clone rather than incremental update"); return false; } }); - if(canUpdate) - return update(build,launcher,workspace,listener,changelogFile); - else - return clone(build,launcher,workspace,listener,changelogFile); + if (canUpdate) { + return update(build, launcher, workspace, listener, changelogFile); + } else { + return clone(build, launcher, workspace, listener, changelogFile); + } } /** * Updates the current workspace. */ - private boolean update(AbstractBuild build, Launcher launcher, FilePath workspace, BuildListener listener, File changelogFile) throws InterruptedException, IOException { - if(clean) { - if (launcher.launch().cmds(findHgExe(listener), "update", "-C", ".") - .envs(build.getEnvironment(listener)).stdout(listener) - .pwd(workspace).join() != 0) { + private boolean update(final AbstractBuild build, + final Launcher launcher, final FilePath workspace, + final BuildListener listener, final File changelogFile) + throws InterruptedException, IOException { + if (clean) { + if (launcher.launch() + .cmds(findHgExe(listener), "update", "-C", ".").envs( + build.getEnvironment(listener)).stdout(listener) + .pwd(workspace).join() != 0) { listener.error("Failed to clobber local modifications"); return false; } - if (launcher.launch().cmds(findHgExe(listener), "--config", "extensions.purge=", "clean", "--all") - .envs(build.getEnvironment(listener)).stdout(listener).pwd(workspace).join() != 0) { + if (launcher.launch().cmds(findHgExe(listener), "--config", + "extensions.purge=", "clean", "--all").envs( + build.getEnvironment(listener)).stdout(listener).pwd( + workspace).join() != 0) { listener.error("Failed to clean unversioned files"); return false; } } - FilePath hgBundle = new FilePath(workspace, "hg.bundle"); + final FilePath hgBundle = new FilePath(workspace, "hg.bundle"); // delete the file prior to "hg incoming", // as one user reported that it causes a failure. @@ -333,20 +356,22 @@ hgBundle.delete(); // calc changelog and create bundle - FileOutputStream os = new FileOutputStream(changelogFile); + final FileOutputStream os = new FileOutputStream(changelogFile); os.write("\n".getBytes()); int r; try { - ArgumentListBuilder args = new ArgumentListBuilder(); - args.add(findHgExe(listener),"incoming","--quiet","--bundle","hg.bundle"); + final ArgumentListBuilder args = new ArgumentListBuilder(); + args.add(findHgExe(listener), "incoming", "--quiet", "--bundle", + "hg.bundle"); String template; - if(isHg10orLater()) { + if (isHg10orLater()) { template = MercurialChangeSet.CHANGELOG_TEMPLATE_10x; } else { template = MercurialChangeSet.CHANGELOG_TEMPLATE_09x; - // Pre-1.0 Hg fails to honor {file_adds} and {file_dels} without --debug. + // Pre-1.0 Hg fails to honor {file_adds} and {file_dels} without + // --debug. args.add("--debug"); } @@ -354,23 +379,29 @@ args.add("-r", getBranch()); - ByteArrayOutputStream errorLog = new ByteArrayOutputStream(); + final ByteArrayOutputStream errorLog = new ByteArrayOutputStream(); - // mercurial produces text in the platform default encoding, so we need to + // mercurial produces text in the platform default encoding, so we + // need to // convert it back to UTF-8 - WriterOutputStream o = new WriterOutputStream(new OutputStreamWriter(os, "UTF-8")); + final WriterOutputStream o = new WriterOutputStream( + new OutputStreamWriter(os, "UTF-8")); try { - r = launcher.launch().cmds(args).envs(build.getEnvironment(listener)) - .stdout(new ForkOutputStream(o,errorLog)).pwd(workspace).join(); + r = launcher.launch().cmds(args).envs( + build.getEnvironment(listener)).stdout( + new ForkOutputStream(o, errorLog)).pwd(workspace) + .join(); } finally { o.flush(); // make sure to commit all output } - if(r!=0 && r!=1) {// 0.9.4 returns 1 for no changes - Util.copyStream(new ByteArrayInputStream(errorLog.toByteArray()),listener.getLogger()); + if (r != 0 && r != 1) {// 0.9.4 returns 1 for no changes + Util.copyStream( + new ByteArrayInputStream(errorLog.toByteArray()), + listener.getLogger()); listener.error("Failed to determine incoming changes"); return false; } - } catch (IOException e) { + } catch (final IOException e) { listener.error("Failed to pull"); return false; } finally { @@ -379,26 +410,28 @@ } // pull - if(r==0 && hgBundle.exists()) - // if incoming didn't fetch anything, it will return 1. That was for 0.9.3. + if (r == 0 && hgBundle.exists()) { + // if incoming didn't fetch anything, it will return 1. That was for + // 0.9.3. // in 0.9.4 apparently it returns 0. try { - if(launcher.launch() - .cmds(findHgExe(listener),"pull","hg.bundle") - .envs(build.getEnvironment(listener)).stdout(listener).pwd(workspace).join()!=0) { + if (launcher.launch().cmds(findHgExe(listener), "pull", + "hg.bundle").envs(build.getEnvironment(listener)) + .stdout(listener).pwd(workspace).join() != 0) { listener.error("Failed to pull"); return false; } - if(launcher.launch() - .cmds(findHgExe(listener),"up","-C", "-r", getBranch()) - .envs(build.getEnvironment(listener)).stdout(listener).pwd(workspace).join()!=0) { + if (launcher.launch().cmds(findHgExe(listener), "up", "-C", + "-r", getBranch()).envs(build.getEnvironment(listener)) + .stdout(listener).pwd(workspace).join() != 0) { listener.error("Failed to update"); return false; } - } catch (IOException e) { + } catch (final IOException e) { listener.error("Failed to pull"); return false; } + } hgBundle.delete(); // do not leave it in workspace @@ -407,17 +440,21 @@ return true; } - private void addTagActionToBuild(AbstractBuild build, Launcher launcher, FilePath workspace, BuildListener listener) throws IOException, InterruptedException { - ByteArrayOutputStream rev = new ByteArrayOutputStream(); - if (launcher.launch().cmds(findHgExe(listener), "log", "-r", ".", "--template", "{node}") - .pwd(workspace).stdout(rev).join()!=0) { + private void addTagActionToBuild(final AbstractBuild build, + final Launcher launcher, final FilePath workspace, + final BuildListener listener) throws IOException, + InterruptedException { + final ByteArrayOutputStream rev = new ByteArrayOutputStream(); + if (launcher.launch().cmds(findHgExe(listener), "log", "-r", ".", + "--template", "{node}").pwd(workspace).stdout(rev).join() != 0) { listener.error("Failed to id"); listener.getLogger().write(rev.toByteArray()); throw new AbortException(); } else { - String id = rev.toString(); - if(!REVISIONID_PATTERN.matcher(id).matches()) { - listener.error("Expected to get an id but got "+id+" instead."); + final String id = rev.toString(); + if (!REVISIONID_PATTERN.matcher(id).matches()) { + listener.error("Expected to get an id but got " + id + + " instead."); throw new AbortException(); } build.addAction(new MercurialTagAction(id)); @@ -430,45 +467,51 @@ private boolean isHg10orLater() { boolean hg10 = false; try { - String v = getDescriptor().findHgVersion(); + final String v = getDescriptor().findHgVersion(); try { - if (v != null && new VersionNumber(v).compareTo(new VersionNumber("1.0"))>=0) { + if (v != null + && new VersionNumber(v).compareTo(new VersionNumber( + "1.0")) >= 0) { hg10 = true; } - } catch (IllegalArgumentException e) { - LOGGER.log(Level.INFO,"Failed to parse Mercurial version number: "+v,e); + } catch (final IllegalArgumentException e) { + LOGGER.log(Level.INFO, + "Failed to parse Mercurial version number: " + v, e); } - } catch (IOException x) { + } catch (final IOException x) { // don't know, never mind - } catch (InterruptedException x) { + } catch (final InterruptedException x) { // ditto } return hg10; } - /** * Start from scratch and clone the whole repository. */ - private boolean clone(AbstractBuild build, Launcher launcher, FilePath workspace, BuildListener listener, File changelogFile) throws InterruptedException, IOException { + private boolean clone(final AbstractBuild build, + final Launcher launcher, final FilePath workspace, + final BuildListener listener, final File changelogFile) + throws InterruptedException, IOException { try { workspace.deleteRecursive(); - } catch (IOException e) { + } catch (final IOException e) { e.printStackTrace(listener.error("Failed to clean the workspace")); return false; } - ArgumentListBuilder args = new ArgumentListBuilder(); - args.add(findHgExe(listener),"clone"); + final ArgumentListBuilder args = new ArgumentListBuilder(); + args.add(findHgExe(listener), "clone"); args.add("-r", getBranch()); - args.add(source,workspace.getRemote()); + args.add(source, workspace.getRemote()); try { - if(launcher.launch().cmds(args).envs(build.getEnvironment(listener)).stdout(listener).join()!=0) { - listener.error("Failed to clone "+source); + if (launcher.launch().cmds(args).envs( + build.getEnvironment(listener)).stdout(listener).join() != 0) { + listener.error("Failed to clone " + source); return false; } - } catch (IOException e) { - e.printStackTrace(listener.error("Failed to clone "+source)); + } catch (final IOException e) { + e.printStackTrace(listener.error("Failed to clone " + source)); return false; } @@ -478,15 +521,17 @@ } @Override - public void buildEnvVars(AbstractBuild build, Map env) { - MercurialTagAction a = build.getAction(MercurialTagAction.class); - if (a!=null) - env.put("MERCURIAL_REVISION",a.id); + public void buildEnvVars(final AbstractBuild build, + final Map env) { + final MercurialTagAction a = build.getAction(MercurialTagAction.class); + if (a != null) { + env.put("MERCURIAL_REVISION", a.id); + } } @Override public ChangeLogParser createChangeLogParser() { - return new MercurialChangeLogParser(); + return new MercurialChangeLogParser(getModulesSet()); } @Override @@ -498,34 +543,63 @@ return modules; } + private Set getModulesSet() { + if (_modules == null) { + _modules = new HashSet(); + final String[] r = modules.split("(? { + public static final class DescriptorImpl extends + SCMDescriptor { private String hgExe; + private transient String version; public DescriptorImpl() { super(HgBrowser.class); load(); } - + /** * {@inheritDoc} * - * Due to compatibility issues with older version we implement this ourselves instead of relying - * on the parent method. Koshuke implemented a fix for this in the core (r21961), so we may drop - * this function after 1.325 is released. + * Due to compatibility issues with older version we implement this + * ourselves instead of relying on the parent method. Koshuke + * implemented a fix for this in the core (r21961), so we may drop this + * function after 1.325 is released. * * @todo: remove this function after 1.325 is released. * - * @see #4514 - * @see core fix + * @see #4514 + * @see core + * fix */ @Override public List>> getBrowserDescriptors() { return RepositoryBrowsers.filter(HgBrowser.class); } - + + @Override public String getDisplayName() { return "Mercurial"; } @@ -534,68 +608,62 @@ * Path to mercurial executable. */ public String getHgExe() { - if(hgExe==null) return "hg"; + if (hgExe == null) { + return "hg"; + } return hgExe; } @Override - public SCM newInstance(StaplerRequest req, JSONObject formData) throws FormException { + public SCM newInstance(final StaplerRequest req, + final JSONObject formData) throws FormException { return super.newInstance(req, formData); } @Override - public boolean configure(StaplerRequest req, JSONObject json) throws FormException { + public boolean configure(final StaplerRequest req, final JSONObject json) + throws FormException { hgExe = req.getParameter("mercurial.hgExe"); version = null; save(); return true; } - /* XXX restore insofar as that is possible: - public FormValidation doHgExeCheck(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException { - return FormValidation.validateExecutable(req.getParameter("value"), new FormValidation.FileValidator() { - public FormValidation validate(File f) { - try { - String v = findHgVersion(); - if(v != null) { - try { - if(new VersionNumber(v).compareTo(V0_9_4)>=0) { - return FormValidation.ok(); // right version - } else { - return FormValidation.error("This hg is ver."+v+" but we need 0.9.4+"); - } - } catch (IllegalArgumentException e) { - return FormValidation.warning("Hudson can't tell if this hg is 0.9.4 or later (detected version is %s)",v); - } - } - v = findHgVersion(UUID_VERSION_STRING); - if(v!=null) { - return FormValidation.warning("Hudson can't tell if this hg is 0.9.4 or later (detected version is %s)",v); - } - } catch (IOException e) { - // failed - } catch (InterruptedException e) { - // failed - } - return FormValidation.error("Unable to check hg version"); - } - }); - } - */ + /* + * XXX restore insofar as that is possible: public FormValidation + * doHgExeCheck(StaplerRequest req, StaplerResponse rsp) throws + * IOException, ServletException { return + * FormValidation.validateExecutable(req.getParameter("value"), new + * FormValidation.FileValidator() { public FormValidation validate(File + * f) { try { String v = findHgVersion(); if(v != null) { try { if(new + * VersionNumber(v).compareTo(V0_9_4)>=0) { return FormValidation.ok(); + * // right version } else { return + * FormValidation.error("This hg is ver."+v+" but we need 0.9.4+"); } } + * catch (IllegalArgumentException e) { returnFormValidation.warning( + * "Hudson can't tell if this hg is 0.9.4 or later (detected version is %s)" + * ,v); } } v = findHgVersion(UUID_VERSION_STRING); if(v!=null) { return + * FormValidation.warning( + * "Hudson can't tell if this hg is 0.9.4 or later (detected version is %s)" + * ,v); } } catch (IOException e) { // failed } catch + * (InterruptedException e) { // failed } return + * FormValidation.error("Unable to check hg version"); } }); } + */ private String findHgVersion() throws IOException, InterruptedException { return findHgVersion(VERSION_STRING); } - private String findHgVersion(Pattern p) throws IOException, InterruptedException { + private String findHgVersion(final Pattern p) throws IOException, + InterruptedException { if (version != null) { return version; } - ByteBuffer baos = new ByteBuffer(); - Proc proc = Hudson.getInstance().createLauncher(TaskListener.NULL).launch() - .cmds(getHgExe(), "version").stdout(baos).start(); + final ByteBuffer baos = new ByteBuffer(); + final Proc proc = Hudson.getInstance().createLauncher( + TaskListener.NULL).launch().cmds(getHgExe(), "version") + .stdout(baos).start(); proc.join(); - Matcher m = p.matcher(baos.toString()); + final Matcher m = p.matcher(baos.toString()); if (m.find()) { version = m.group(1); return version; @@ -607,17 +675,19 @@ /** * Pattern matcher for the version number. */ - private static final Pattern VERSION_STRING = Pattern.compile("\\(version ([0-9.]+)"); + private static final Pattern VERSION_STRING = Pattern + .compile("\\(version ([0-9.]+)"); } - private static final long serialVersionUID = 1L; - private static final Logger LOGGER = Logger.getLogger(MercurialSCM.class.getName()); + private static final Logger LOGGER = Logger.getLogger(MercurialSCM.class + .getName()); /** * Pattern that matches revision ID. */ - private static final Pattern REVISIONID_PATTERN = Pattern.compile("[0-9a-f]{40}"); + private static final Pattern REVISIONID_PATTERN = Pattern + .compile("[0-9a-f]{40}"); } Index: src/main/java/hudson/plugins/mercurial/MercurialChangeLogParser.java =================================================================== --- src/main/java/hudson/plugins/mercurial/MercurialChangeLogParser.java (revision 23087) +++ src/main/java/hudson/plugins/mercurial/MercurialChangeLogParser.java (working copy) @@ -4,44 +4,77 @@ import hudson.scm.ChangeLogParser; import hudson.util.Digester2; import hudson.util.IOException2; -import org.apache.commons.digester.Digester; -import org.xml.sax.SAXException; import java.io.File; import java.io.IOException; import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; +import org.apache.commons.digester.Digester; +import org.xml.sax.SAXException; + /** * Parses the changelog.xml. - * + * * See {@link MercurialChangeSet#CHANGELOG_TEMPLATE} for the format. * * @author Kohsuke Kawaguchi */ public class MercurialChangeLogParser extends ChangeLogParser { - public MercurialChangeSetList parse(AbstractBuild build, File changelogFile) throws IOException, SAXException { - Digester digester = new Digester2(); - ArrayList r = new ArrayList(); + private Set modules = new HashSet(); + + MercurialChangeLogParser(final Set modules) { + this.modules = modules; + } + + @Override + public MercurialChangeSetList parse(final AbstractBuild build, + final File changelogFile) throws IOException, SAXException { + final Digester digester = new Digester2(); + final ArrayList r = new ArrayList(); digester.push(r); digester.addObjectCreate("*/changeset", MercurialChangeSet.class); digester.addSetProperties("*/changeset"); - digester.addSetProperties("*/changeset","author","user"); + digester.addSetProperties("*/changeset", "author", "user"); digester.addBeanPropertySetter("*/changeset/msg"); digester.addBeanPropertySetter("*/changeset/added"); digester.addBeanPropertySetter("*/changeset/deleted"); digester.addBeanPropertySetter("*/changeset/files"); digester.addBeanPropertySetter("*/changeset/parents"); - digester.addSetNext("*/changeset","add"); + digester.addSetNext("*/changeset", "add"); try { digester.parse(changelogFile); - } catch (IOException e) { - throw new IOException2("Failed to parse "+changelogFile,e); - } catch (SAXException e) { - throw new IOException2("Failed to parse "+changelogFile,e); + } catch (final IOException e) { + throw new IOException2("Failed to parse " + changelogFile, e); + } catch (final SAXException e) { + throw new IOException2("Failed to parse " + changelogFile, e); } + final ArrayList filtered = filterChangesSetForModule(r); + return new MercurialChangeSetList(build, filtered); + } - return new MercurialChangeSetList(build,r); + private ArrayList filterChangesSetForModule( + final ArrayList r) { + if (modules == null) { + return r; + } + final ArrayList filtered = new ArrayList(); + + for (final MercurialChangeSet cs : r) { + final Collection affectedPaths = cs.getAffectedPaths(); + for (final String path : affectedPaths) { + for (final String dependency : modules) { + if (path.startsWith(dependency)) { + filtered.add(cs); + break; + } + } + } + } + return filtered; } }