001/*
002 * Sonar, open source software quality management tool.
003 * Copyright (C) 2008-2012 SonarSource
004 * mailto:contact AT sonarsource DOT com
005 *
006 * Sonar is free software; you can redistribute it and/or
007 * modify it under the terms of the GNU Lesser General Public
008 * License as published by the Free Software Foundation; either
009 * version 3 of the License, or (at your option) any later version.
010 *
011 * Sonar is distributed in the hope that it will be useful,
012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014 * Lesser General Public License for more details.
015 *
016 * You should have received a copy of the GNU Lesser General Public
017 * License along with Sonar; if not, write to the Free Software
018 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
019 */
020package org.sonar.plugins.jacoco;
021
022import org.apache.tools.ant.*;
023import org.sonar.api.batch.CoverageExtension;
024import org.sonar.api.batch.Initializer;
025import org.sonar.api.batch.SupportedEnvironment;
026import org.sonar.api.resources.Project;
027
028import java.util.Map;
029
030@SupportedEnvironment("ant")
031public class JacocoAntInitializer extends Initializer implements CoverageExtension {
032
033  private final TaskEnhancer[] taskEnhancers = new TaskEnhancer[] { new JavaLikeTaskEnhancer("java"), new JavaLikeTaskEnhancer("junit"), new TestngTaskEnhancer() };
034
035  private org.apache.tools.ant.Project antProject;
036  private JacocoConfiguration configuration;
037
038  public JacocoAntInitializer(org.apache.tools.ant.Project antProject, JacocoConfiguration configuration) {
039    this.antProject = antProject;
040    this.configuration = configuration;
041  }
042
043  @Override
044  public boolean shouldExecuteOnProject(org.sonar.api.resources.Project project) {
045    return project.getAnalysisType().equals(Project.AnalysisType.DYNAMIC);
046  }
047
048  @Override
049  public void execute(org.sonar.api.resources.Project project) {
050    Map<String, Target> hastable = antProject.getTargets();
051    String jvmArg = configuration.getJvmArgument();
052    String[] names = configuration.getAntTargets();
053    for (String name : names) {
054      Target target = hastable.get(name);
055      if (target == null) {
056        JaCoCoUtils.LOG.warn("Target '{}' not found", name);
057      } else {
058        // Enhance target
059        for (Task task : target.getTasks()) {
060          for (TaskEnhancer enhancer : taskEnhancers) {
061            if (enhancer.supportsTask(task)) {
062              enhancer.enhanceTask(task, jvmArg);
063            }
064          }
065        }
066        // Execute target
067        target.performTasks();
068      }
069    }
070  }
071
072  private static class TestngTaskEnhancer extends TaskEnhancer {
073    @Override
074    public boolean supportsTask(Task task) {
075      return "testng".equals(task.getTaskName());
076    }
077  }
078
079  /**
080   * Basic task enhancer that can handle all 'java like' tasks. That is, tasks
081   * that have a top level fork attribute and nested jvmargs elements
082   */
083  private static class JavaLikeTaskEnhancer extends TaskEnhancer {
084    private String taskName;
085
086    public JavaLikeTaskEnhancer(String taskName) {
087      this.taskName = taskName;
088    }
089
090    @Override
091    public boolean supportsTask(final Task task) {
092      return taskName.equals(task.getTaskName());
093    }
094
095    @Override
096    public void enhanceTask(final Task task, final String jvmArg) {
097      final RuntimeConfigurable configurableWrapper = task.getRuntimeConfigurableWrapper();
098
099      final String forkValue = (String) configurableWrapper.getAttributeMap().get("fork");
100
101      if (forkValue == null || !org.apache.tools.ant.Project.toBoolean(forkValue)) {
102        throw new BuildException("Coverage can only be applied on a forked VM");
103      }
104
105      super.enhanceTask(task, jvmArg);
106    }
107
108  }
109
110  private abstract static class TaskEnhancer {
111    /**
112     * @param task Task instance to enhance
113     * @return <code>true</code> if this enhancer is capable of enhancing the requested task
114     */
115    public abstract boolean supportsTask(Task task);
116
117    /**
118     * Attempt to enhance the supplied task with coverage information. This
119     * operation may fail if the task is being executed in the current VM
120     * 
121     * @param task Task instance to enhance (usually an {@link UnknownElement})
122     * @param jvmArg
123     * @throws BuildException Thrown if this enhancer can handle this type of task, but this instance can not be enhanced for some reason.
124     */
125    public void enhanceTask(Task task, String jvmArg) {
126      addJvmArg((UnknownElement) task, jvmArg);
127    }
128
129    public void addJvmArg(final UnknownElement task, final String jvmArg) {
130      final UnknownElement el = new UnknownElement("jvmarg");
131      el.setTaskName("jvmarg");
132      el.setQName("jvmarg");
133
134      final RuntimeConfigurable runtimeConfigurableWrapper = el.getRuntimeConfigurableWrapper();
135      runtimeConfigurableWrapper.setAttribute("value", jvmArg);
136
137      task.getRuntimeConfigurableWrapper().addChild(runtimeConfigurableWrapper);
138
139      task.addChild(el);
140    }
141  }
142
143}