merge BuildExecutionTimer and MultiModuleLogger
diff --git a/src/main/java/org/apache/easyant/core/ant/listerners/BuildExecutionTimer.java b/src/main/java/org/apache/easyant/core/ant/listerners/BuildExecutionTimer.java
deleted file mode 100644
index fe328ec..0000000
--- a/src/main/java/org/apache/easyant/core/ant/listerners/BuildExecutionTimer.java
+++ /dev/null
@@ -1,166 +0,0 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one or more
- *  contributor license agreements.  See the NOTICE file distributed with
- *  this work for additional information regarding copyright ownership.
- *  The ASF licenses this file to You under the Apache License, Version 2.0
- *  (the "License"); you may not use this file except in compliance with
- *  the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS,
- *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
- *
- */
-package org.apache.easyant.core.ant.listerners;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.apache.easyant.core.EasyAntMagicNames;
-import org.apache.easyant.core.ant.ExecutionStatus;
-import org.apache.ivy.util.StringUtils;
-import org.apache.tools.ant.BuildEvent;
-import org.apache.tools.ant.BuildListener;
-import org.apache.tools.ant.Project;
-import org.apache.tools.ant.SubBuildListener;
-
-/*
- * This listener is created so that it may be used to provide all statistic
- * collection for easyant builds.
- */
-
-/**
- * Build listener to time execution of builds.
- */
-public class BuildExecutionTimer implements BuildListener, SubBuildListener {
-
-    /**
-     * Reference key against which build execution results will be stored
-     */
-    public static final String EXECUTION_TIMER_BUILD_RESULTS = "execution.timer.build.results";
-
-    /**
-     * Reference key against which sub-build execution times will be stored
-     */
-    public static final String EXECUTION_TIMER_SUBBUILD_RESULTS = "execution.timer.subbuild.results";
-
-    private static final String DEMARKER = "======================================================================";
-
-    // build start time
-    // to be initialized in buildStarted method
-    private long buildStartTime;
-
-    /**
-     * stops the timer and stores the result as a project reference by the key 'referenceName'
-     */
-    protected void stopTimer(BuildEvent event, String referenceName, long startTime) {
-        List<ExecutionResult> results = event.getProject().getReference(referenceName);
-        if (results == null) {
-            results = new ArrayList<ExecutionResult>();
-            event.getProject().addReference(referenceName, results);
-        }
-        ExecutionStatus status = ExecutionStatus.SUCCESS;
-        if (event.getException() != null) {
-            status = ExecutionStatus.FAILED;
-        } else if (event.getProject().getProperty(EasyAntMagicNames.PROJECT_EXECUTED_TARGETS) == null) {
-            status = ExecutionStatus.SKIPPED;
-        }
-
-        ExecutionResult execResult = new ExecutionResult(event.getProject().getName(), System.currentTimeMillis()
-                - startTime, status);
-
-        results.add(execResult);
-
-    }
-
-    public void buildFinished(BuildEvent event) {
-        stopTimer(event, EXECUTION_TIMER_BUILD_RESULTS, buildStartTime);
-        printExecutionSubBuildsExecutionTimes(event.getProject());
-    }
-
-    private void printExecutionSubBuildsExecutionTimes(Project project) {
-        String lineSep = org.apache.tools.ant.util.StringUtils.LINE_SEP;
-        List<ExecutionResult> allSubBuildResults = project.getReference(EXECUTION_TIMER_SUBBUILD_RESULTS);
-        if (allSubBuildResults != null && allSubBuildResults.size() > 0) {
-            StringBuilder sb = new StringBuilder();
-            sb.append(lineSep).append(DEMARKER).append(lineSep);
-            sb.append("Project Sub-modules Summary: ").append(lineSep).append(DEMARKER);
-            sb.append(lineSep).append(formatExecutionResults(allSubBuildResults));
-            sb.append(lineSep).append(DEMARKER);
-            project.log(sb.toString());
-        }
-    }
-
-    public void buildStarted(BuildEvent arg0) {
-        buildStartTime = System.currentTimeMillis();
-    }
-
-    public void messageLogged(BuildEvent arg0) {
-        // leaving empty till better use is found
-    }
-
-    public void targetFinished(BuildEvent arg0) {
-        // leaving empty till better use is found
-    }
-
-    /* following methods may be implemented for more comprehensive stats */
-    public void targetStarted(BuildEvent arg0) {
-        // leaving empty till better use is found
-    }
-
-    public void taskFinished(BuildEvent arg0) {
-        // leaving empty till better use is found
-    }
-
-    public void taskStarted(BuildEvent arg0) {
-        // leaving empty till better use is found
-    }
-
-    /**
-     * Returns a string containing results of execution timing for display on console in a tabular fashion
-     * 
-     * @param results
-     * @return
-     */
-    public String formatExecutionResults(List<ExecutionResult> results) {
-        String formattedResults;
-        int constantSpaces = 10;
-        int maxUnitNameLength = 0;
-        int maxExecTimeLength = 0;
-        for (ExecutionResult result : results) {
-            maxUnitNameLength = result.getUnitName().length() > maxUnitNameLength ? result.getUnitName().length()
-                    : maxUnitNameLength;
-            maxExecTimeLength = result.getFormattedElapsedTime().length() > maxExecTimeLength ? result
-                    .getFormattedElapsedTime().length() : maxExecTimeLength;
-        }
-        StringBuilder sb = new StringBuilder(org.apache.tools.ant.util.StringUtils.LINE_SEP);
-        for (ExecutionResult result : results) {
-            String moduleName = result.getUnitName();
-            int variableSpaces = maxUnitNameLength - moduleName.length() + constantSpaces;
-            sb.append(" * ").append(result.getUnitName()).append(StringUtils.repeat(" ", variableSpaces));
-            // keeping both success and failed strings of equal length
-            String execResult = result.getStatus().toString();
-            if (execResult.length() < 7) {
-                execResult += StringUtils.repeat(" ", 7 - execResult.length());
-            }
-            sb.append(execResult).append(" [took ").append(result.getFormattedElapsedTime()).append("]")
-                    .append(org.apache.tools.ant.util.StringUtils.LINE_SEP);
-        }
-
-        formattedResults = sb.toString();
-        return formattedResults;
-    }
-
-    public void subBuildFinished(BuildEvent arg0) {
-        stopTimer(arg0, EXECUTION_TIMER_SUBBUILD_RESULTS, buildStartTime);
-    }
-
-    public void subBuildStarted(BuildEvent arg0) {
-        buildStartTime = System.currentTimeMillis();
-    }
-
-}
diff --git a/src/main/java/org/apache/easyant/core/ant/listerners/MultiModuleLogger.java b/src/main/java/org/apache/easyant/core/ant/listerners/MultiModuleLogger.java
index 2a262f4..a6b9125 100644
--- a/src/main/java/org/apache/easyant/core/ant/listerners/MultiModuleLogger.java
+++ b/src/main/java/org/apache/easyant/core/ant/listerners/MultiModuleLogger.java
@@ -17,31 +17,35 @@
  */
 package org.apache.easyant.core.ant.listerners;
 
-import java.io.File;
-
+import org.apache.easyant.core.EasyAntMagicNames;
+import org.apache.easyant.core.ant.ExecutionStatus;
 import org.apache.tools.ant.BuildEvent;
 import org.apache.tools.ant.Project;
+import org.apache.tools.ant.SubBuildListener;
 import org.apache.tools.ant.listener.TimestampedLogger;
 import org.apache.tools.ant.util.StringUtils;
 
-public class MultiModuleLogger extends DefaultEasyAntLogger {
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
 
+public class MultiModuleLogger extends DefaultEasyAntLogger implements SubBuildListener {
+
+
+    /**
+     * Reference key against which build execution results will be stored
+     */
+    public static final String EXECUTION_TIMER_BUILD_RESULTS = "execution.timer.build.results";
+
+    private static final String DEMARKER = "======================================================================";
     private volatile boolean subBuildStartedRaised = false;
     private final Object subBuildLock = new Object();
-
-    /**
-     * Header string for the log. * {@value}
-     */
-    public static final String HEADER = "======================================================================";
-    /**
-     * Footer string for the log. * {@value}
-     */
-    public static final String FOOTER = HEADER;
+    private long buildStartTime;
 
     /**
      * This is an override point: the message that indicates whether a build failed. Subclasses can change/enhance the
      * message.
-     * 
+     *
      * @return The classic "BUILD FAILED" plus a timestamp
      */
     protected String getBuildFailedMessage() {
@@ -51,75 +55,58 @@
     /**
      * This is an override point: the message that indicates that a build succeeded. Subclasses can change/enhance the
      * message.
-     * 
+     *
      * @return The classic "BUILD SUCCESSFUL" plus a timestamp
      */
     protected String getBuildSuccessfulMessage() {
         return super.getBuildSuccessfulMessage() + TimestampedLogger.SPACER + getTimestamp();
     }
 
-    /**
-     * {@inheritDoc}
-     * 
-     * @param event
-     */
     public void targetStarted(BuildEvent event) {
         maybeRaiseSubBuildStarted(event);
         targetName = extractTargetName(event);
     }
 
-    /**
-     * {@inheritDoc}
-     * 
-     * @param event
-     */
     public void taskStarted(BuildEvent event) {
         maybeRaiseSubBuildStarted(event);
         super.taskStarted(event);
     }
 
-    /**
-     * {@inheritDoc}
-     * 
-     * @param event
-     */
     public void buildFinished(BuildEvent event) {
+        stopTimer(event);
+        printExecutionSubBuildsExecutionTimes(event.getProject());
         maybeRaiseSubBuildStarted(event);
         subBuildFinished(event);
         super.buildFinished(event);
     }
 
-    /**
-     * {@inheritDoc}
-     * 
-     * @param event
-     */
     public void messageLogged(BuildEvent event) {
         maybeRaiseSubBuildStarted(event);
         super.messageLogged(event);
     }
 
-    /**
-     * {@inheritDoc}
-     * 
-     * @param event
-     *            An event with any relevant extra information. Must not be <code>null</code>.
-     */
     public void subBuildStarted(BuildEvent event) {
+        initTimer(event.getProject());
         String name = extractNameOrDefault(event);
         Project project = event.getProject();
 
         File base = project == null ? null : project.getBaseDir();
         String path = (base == null) ? "With no base directory" : "In " + base.getAbsolutePath();
-        printMessage(StringUtils.LINE_SEP + getHeader() + StringUtils.LINE_SEP + "Entering project " + name
-                + StringUtils.LINE_SEP + path + StringUtils.LINE_SEP + getFooter(), out, event.getPriority());
+        printMessage(StringUtils.LINE_SEP + DEMARKER + StringUtils.LINE_SEP + "Entering project " + name
+                + StringUtils.LINE_SEP + path + StringUtils.LINE_SEP + DEMARKER, out, event.getPriority());
+    }
+
+
+    @Override
+    public void buildStarted(BuildEvent event) {
+        initTimer(event.getProject());
+        super.buildStarted(event);
     }
 
     /**
      * Get the name of an event
-     * 
-     * @param event
-     *            the event name
+     *
+     * @param event the event name
      * @return the name or a default string
      */
     protected String extractNameOrDefault(BuildEvent event) {
@@ -132,31 +119,14 @@
         return name;
     }
 
-    /** {@inheritDoc} */
     public void subBuildFinished(BuildEvent event) {
+        stopTimer(event);
         String name = extractNameOrDefault(event);
         String failed = event.getException() != null ? "failing " : "";
-        printMessage(StringUtils.LINE_SEP + getHeader() + StringUtils.LINE_SEP + "Exiting " + failed + "project "
-                + name + StringUtils.LINE_SEP + getFooter(), out, event.getPriority());
+        printMessage(StringUtils.LINE_SEP + DEMARKER + StringUtils.LINE_SEP + "Exiting " + failed + "project "
+                + name + StringUtils.LINE_SEP + DEMARKER, out, event.getPriority());
     }
 
-    /**
-     * Override point: return the header string for the entry/exit message
-     * 
-     * @return the header string
-     */
-    protected String getHeader() {
-        return HEADER;
-    }
-
-    /**
-     * Override point: return the footer string for the entry/exit message
-     * 
-     * @return the footer string
-     */
-    protected String getFooter() {
-        return FOOTER;
-    }
 
     private void maybeRaiseSubBuildStarted(BuildEvent event) {
         // double checked locking should be OK since the flag is write-once
@@ -172,9 +142,8 @@
 
     /**
      * Override point, extract the target name
-     * 
-     * @param event
-     *            the event to work on
+     *
+     * @param event the event to work on
      * @return the target name -including the owning project name (if non-null)
      */
     protected String extractTargetName(BuildEvent event) {
@@ -187,4 +156,72 @@
         }
     }
 
+    private void initTimer(Project project) {
+        buildStartTime = System.currentTimeMillis();
+        project.addReference(EXECUTION_TIMER_BUILD_RESULTS, new ArrayList<ExecutionResult>());
+    }
+
+    /**
+     * stops the timer and stores the result as a project reference by the key 'referenceName'
+     */
+    private void stopTimer(BuildEvent event) {
+        List<ExecutionResult> results = event.getProject().getReference(EXECUTION_TIMER_BUILD_RESULTS);
+        ExecutionStatus status = ExecutionStatus.SUCCESS;
+        if (event.getException() != null) {
+            status = ExecutionStatus.FAILED;
+        } else if (event.getProject().getProperty(EasyAntMagicNames.PROJECT_EXECUTED_TARGETS) == null) {
+            status = ExecutionStatus.SKIPPED;
+        }
+
+        ExecutionResult execResult = new ExecutionResult(event.getProject().getName(), System.currentTimeMillis()
+                - buildStartTime, status);
+
+        results.add(execResult);
+
+    }
+
+    private void printExecutionSubBuildsExecutionTimes(Project project) {
+        String lineSep = org.apache.tools.ant.util.StringUtils.LINE_SEP;
+        List<ExecutionResult> allSubBuildResults = project.getReference(EXECUTION_TIMER_BUILD_RESULTS);
+        if (allSubBuildResults != null && !allSubBuildResults.isEmpty()) {
+            StringBuilder sb = new StringBuilder();
+            sb.append(lineSep).append(DEMARKER).append(lineSep);
+            sb.append("Project Sub-modules Summary: ").append(lineSep).append(DEMARKER);
+            sb.append(lineSep).append(formatExecutionResults(allSubBuildResults));
+            project.log(sb.toString());
+        }
+    }
+
+    private String formatExecutionResults(List<ExecutionResult> results) {
+        String formattedResults;
+        int maxUnitNameLength = 0;
+        int maxExecTimeLength = 0;
+        for (ExecutionResult result : results) {
+            maxUnitNameLength = result.getUnitName().length() > maxUnitNameLength ? result.getUnitName().length()
+                    : maxUnitNameLength;
+            maxExecTimeLength = result.getFormattedElapsedTime().length() > maxExecTimeLength ? result
+                    .getFormattedElapsedTime().length() : maxExecTimeLength;
+        }
+        StringBuilder sb = new StringBuilder(org.apache.tools.ant.util.StringUtils.LINE_SEP);
+        for (ExecutionResult result : results) {
+            String moduleName = padRight(result.getUnitName(), maxUnitNameLength + 10);
+            sb.append(" * ").append(moduleName);
+            // keeping both success and failed strings of equal length
+            String execResult = padRight(result.getStatus().toString(), 7);
+            sb.append(execResult)//
+                    .append(" [ took ")//
+                    .append(padRight(result.getFormattedElapsedTime(), maxExecTimeLength + 1))//
+                    .append("]")
+                    .append(org.apache.tools.ant.util.StringUtils.LINE_SEP);
+        }
+
+        formattedResults = sb.toString();
+        return formattedResults;
+    }
+
+    private String padRight(String string, int nbSpace) {
+        return String.format("%1$-" + nbSpace + "s", string);
+    }
+
+
 }
diff --git a/src/main/java/org/apache/easyant/tasks/SubModule.java b/src/main/java/org/apache/easyant/tasks/SubModule.java
index 877f4ba..3144ef6 100644
--- a/src/main/java/org/apache/easyant/tasks/SubModule.java
+++ b/src/main/java/org/apache/easyant/tasks/SubModule.java
@@ -20,8 +20,8 @@
 import org.apache.easyant.core.EasyAntConstants;
 import org.apache.easyant.core.EasyAntMagicNames;
 import org.apache.easyant.core.ant.ProjectUtils;
-import org.apache.easyant.core.ant.listerners.BuildExecutionTimer;
 import org.apache.easyant.core.ant.listerners.ExecutionResult;
+import org.apache.easyant.core.ant.listerners.MultiModuleLogger;
 import org.apache.easyant.core.ivy.IvyInstanceHelper;
 import org.apache.ivy.ant.IvyPublish;
 import org.apache.ivy.ant.IvyResolve;
@@ -241,7 +241,7 @@
         } finally {
             // add execution times for the current submodule to parent
             // project references for access from MetaBuildExecutor
-            //   storeExecutionTimes(getProject(), subModule);
+            storeExecutionTimes(getProject(), subModule);
         }
 
     }
@@ -256,10 +256,6 @@
             BuildListener buildListener = getProject().getBuildListeners().elementAt(i);
             subModule.addBuildListener(buildListener);
         }
-        // explicitly add the execution timer to time
-        // sub builds
-        subModule.addBuildListener(new BuildExecutionTimer());
-
         // copy all User properties
         addAlmostAll(getProject().getUserProperties(), subModule, PropertyType.USER);
         // inherit meta.target directory, for shared build repository.
@@ -296,13 +292,13 @@
     @SuppressWarnings("unchecked")
     private void storeExecutionTimes(Project parent, Project child) {
         List<ExecutionResult> allresults = parent
-                .getReference(BuildExecutionTimer.EXECUTION_TIMER_SUBBUILD_RESULTS);
+                .getReference(MultiModuleLogger.EXECUTION_TIMER_BUILD_RESULTS);
         if (allresults == null) {
             allresults = new ArrayList<ExecutionResult>();
-            parent.addReference(BuildExecutionTimer.EXECUTION_TIMER_SUBBUILD_RESULTS, allresults);
+            parent.addReference(MultiModuleLogger.EXECUTION_TIMER_BUILD_RESULTS, allresults);
         }
         List<ExecutionResult> childResults = child
-                .getReference(BuildExecutionTimer.EXECUTION_TIMER_SUBBUILD_RESULTS);
+                .getReference(MultiModuleLogger.EXECUTION_TIMER_BUILD_RESULTS);
         if (childResults != null) {
             allresults.addAll(childResults);
         }
diff --git a/src/test/java/org/apache/easyant/tasks/SubModuleTest.java b/src/test/java/org/apache/easyant/tasks/SubModuleTest.java
index a67c083..a0f3c4d 100644
--- a/src/test/java/org/apache/easyant/tasks/SubModuleTest.java
+++ b/src/test/java/org/apache/easyant/tasks/SubModuleTest.java
@@ -18,15 +18,8 @@
 
 package org.apache.easyant.tasks;
 
-import static org.hamcrest.CoreMatchers.notNullValue;
-import static org.junit.Assert.assertThat;
-
-import java.io.File;
-import java.io.IOException;
-import java.net.URISyntaxException;
-
 import org.apache.easyant.core.EasyAntMagicNames;
-import org.apache.easyant.core.ant.listerners.BuildExecutionTimer;
+import org.apache.easyant.core.ant.listerners.MultiModuleLogger;
 import org.apache.tools.ant.Project;
 import org.apache.tools.ant.types.FileSet;
 import org.apache.tools.ant.types.Path;
@@ -36,6 +29,13 @@
 import org.junit.rules.ExpectedException;
 import org.junit.rules.TemporaryFolder;
 
+import java.io.File;
+import java.io.IOException;
+import java.net.URISyntaxException;
+
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.junit.Assert.assertThat;
+
 public class SubModuleTest extends AntTaskBaseTest {
 
     private SubModule submodule;
@@ -156,7 +156,7 @@
         assertLogContaining("Executing [modulewithtarget:mytarget] on module1");
         assertLogContaining("Executing [modulewithtarget:mytarget] on module2");
 
-        assertThat(submodule.getProject().getReference(BuildExecutionTimer.EXECUTION_TIMER_SUBBUILD_RESULTS),
+        assertThat(submodule.getProject().getReference(MultiModuleLogger.EXECUTION_TIMER_BUILD_RESULTS),
                 notNullValue());
     }
 }