com.hello2morrow.sonarplugin.SonarJSensor.java Source code

Java tutorial

Introduction

Here is the source code for com.hello2morrow.sonarplugin.SonarJSensor.java

Source

/*
 * Sonar SonarJ Plugin
 * Copyright (C) 2009, 2010 hello2morrow GmbH
 * mailto: info AT hello2morrow DOT com
 *
 * Licensed 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 com.hello2morrow.sonarplugin;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;

import com.hello2morrow.sonarplugin.xsd.XsdBuildUnits;
import org.apache.commons.configuration.Configuration;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sonar.api.batch.Sensor;
import org.sonar.api.batch.SensorContext;
import org.sonar.api.batch.maven.DependsUponMavenPlugin;
import org.sonar.api.batch.maven.MavenPluginHandler;
import org.sonar.api.measures.Measure;
import org.sonar.api.measures.Metric;
import org.sonar.api.profiles.RulesProfile;
import org.sonar.api.resources.JavaFile;
import org.sonar.api.resources.JavaPackage;
import org.sonar.api.resources.Project;
import org.sonar.api.resources.Resource;
import org.sonar.api.rules.ActiveRule;
import org.sonar.api.rules.Rule;
import org.sonar.api.rules.RulePriority;
import org.sonar.api.rules.RulesManager;
import org.sonar.api.rules.Violation;

import com.hello2morrow.sonarplugin.xsd.ReportContext;
import com.hello2morrow.sonarplugin.xsd.XsdArchitectureViolation;
import com.hello2morrow.sonarplugin.xsd.XsdAttribute;
import com.hello2morrow.sonarplugin.xsd.XsdAttributeCategory;
import com.hello2morrow.sonarplugin.xsd.XsdAttributeRoot;
import com.hello2morrow.sonarplugin.xsd.XsdCycleGroup;
import com.hello2morrow.sonarplugin.xsd.XsdCycleGroups;
import com.hello2morrow.sonarplugin.xsd.XsdCyclePath;
import com.hello2morrow.sonarplugin.xsd.XsdDependencyProblem;
import com.hello2morrow.sonarplugin.xsd.XsdElementProblem;
import com.hello2morrow.sonarplugin.xsd.XsdPosition;
import com.hello2morrow.sonarplugin.xsd.XsdProblemCategory;
import com.hello2morrow.sonarplugin.xsd.XsdProjects;
import com.hello2morrow.sonarplugin.xsd.XsdTask;
import com.hello2morrow.sonarplugin.xsd.XsdTasks;
import com.hello2morrow.sonarplugin.xsd.XsdTypeRelation;
import com.hello2morrow.sonarplugin.xsd.XsdViolations;
import com.hello2morrow.sonarplugin.xsd.XsdWarning;
import com.hello2morrow.sonarplugin.xsd.XsdWarnings;
import com.hello2morrow.sonarplugin.xsd.XsdWarningsByAttribute;
import com.hello2morrow.sonarplugin.xsd.XsdWarningsByAttributeGroup;

public final class SonarJSensor implements Sensor {
    public static final String COST_PER_INDEX_POINT = "sonarj.index_point_cost";

    private static final Logger LOG = LoggerFactory.getLogger(SonarJSensor.class);

    private static final String REPORT_DIR = "sonarj-sonar-plugin";
    private static final String REPORT_NAME = "sonarj-report.xml";

    private static final String ACD = "Average component dependency (ACD)";
    private static final String NCCD = "Normalized cumulative component dependency (NCCD)";
    private static final String INTERNAL_PACKAGES = "Number of internal packages";
    private static final String INSTRUCTIONS = "Number of instructions";
    private static final String UNASSIGNED_TYPES = "Number of unassigned types";
    private static final String VIOLATING_DEPENDENCIES = "Number of violating type dependencies";
    private static final String VIOLATING_TYPES = "Number of violating types";
    private static final String TYPE_DEPENDENCIES = "Number of type dependencies (all)";
    private static final String JAVA_FILES = "Number of Java source files (non-excluded)";
    private static final String IGNORED_VIOLATIONS = "Number of ignored violations";
    private static final String IGNORED_WARNINGS = "Number of ignored warnings";
    private static final String TASKS = "Number of tasks";
    private static final String ALL_WARNINGS = "Number of warnings (all)";
    private static final String CYCLE_WARNINGS = "Number of warnings (cyclic)";
    private static final String THRESHOLD_WARNINGS = "Number of warnings (thresholds)";
    private static final String WORKSPACE_WARNINGS = "Number of warnings (workspace)";
    private static final String DUPLICATE_WARNINGS = "Number of warnings (duplicate code blocks)";
    private static final String EROSION_REFS = "Structural erosion - reference level";
    private static final String EROSION_TYPES = "Structural erosion - type level";
    private static final String INTERNAL_TYPES = "Number of internal types (all)";
    private static final String STUCTURAL_DEBT_INDEX = "Structural debt (index)";

    private Map<String, Number> projectMetrics;
    private SensorContext sensorContext;
    private RulesManager rulesManager;
    private RulesProfile rulesProfile;
    private double indexCost = 12.0;

    protected static ReportContext readSonarjReport(String fileName, String packaging) {
        ReportContext result = null;
        InputStream input = null;
        ClassLoader defaultClassLoader = Thread.currentThread().getContextClassLoader();

        try {
            Thread.currentThread().setContextClassLoader(SonarJSensor.class.getClassLoader());
            JAXBContext context = JAXBContext.newInstance("com.hello2morrow.sonarplugin.xsd");
            Unmarshaller u = context.createUnmarshaller();

            input = new FileInputStream(fileName);
            result = (ReportContext) u.unmarshal(input);
        } catch (JAXBException e) {
            LOG.error("JAXB Problem in " + fileName, e);
        } catch (FileNotFoundException e) {
            if (!packaging.equalsIgnoreCase("pom")) {
                LOG.warn("Cannot open SonarJ report: " + fileName + ".");
                LOG.warn(
                        "  Did you run the maven sonarj goal before with the POM option <prepareForSonar>true</prepareForSonar> "
                                + "or with the commandline option -Dsonarj.prepareForSonar=true?");
                LOG.warn("  Is the project part of the SonarJ architecture description?");
                LOG.warn("  Did you set the 'aggregate' to true (must be false)?");
            }
        } finally {
            Thread.currentThread().setContextClassLoader(defaultClassLoader);
            if (input != null) {
                try {
                    input.close();
                } catch (IOException e) {
                    LOG.error("Cannot close " + fileName, e);
                }
            }
        }
        return result;
    }

    public SonarJSensor(Configuration config, RulesManager rulesManager, RulesProfile rulesProfile) {
        indexCost = config.getDouble(COST_PER_INDEX_POINT, 12.0);
        this.rulesManager = rulesManager;
        this.rulesProfile = rulesProfile;
        if (rulesManager == null) {
            LOG.warn("No RulesManager provided to sensor");
        }
        if (rulesProfile == null) {
            LOG.warn("No RulesProfile given to sensor");
        }
    }

    public boolean shouldExecuteOnProject(Project project) {
        return true;
    }

    private Map<String, Number> readAttributes(XsdAttributeRoot root) {
        Map<String, Number> result = new HashMap<String, Number>();

        for (XsdAttributeCategory cat : root.getAttributeCategory()) {
            for (XsdAttribute attr : cat.getAttribute()) {
                String attrName = attr.getName();
                String value = attr.getValue();

                try {
                    if (value.indexOf('.') >= 0) {
                        result.put(attrName, Double.valueOf(value));
                    } else if (value.indexOf(':') == -1) {
                        result.put(attrName, Integer.valueOf(value));
                    }
                } catch (NumberFormatException e) {
                    // Ignore this value
                }
            }
        }
        return result;
    }

    private double getProjectMetric(String key) {
        Number num = projectMetrics.get(key);

        if (num == null) {
            LOG.error("Cannot find metric <" + key + "> in generated report");
            return 0.0;
        }
        return num.doubleValue();
    }

    private Measure saveMeasure(String key, Metric metric, int precision) {
        double value = getProjectMetric(key);

        return saveMeasure(metric, value, precision);
    }

    private Measure saveMeasure(Metric metric, double value, int precision) {
        Measure m = new Measure(metric, value, precision);

        sensorContext.saveMeasure(m);
        return m;
    }

    private String getAttribute(List<XsdAttribute> map, String name) {
        String value = null;

        for (XsdAttribute attr : map) {
            if (attr.getName().equals(name)) {
                value = attr.getValue();
                break;
            }
        }
        return value;
    }

    @SuppressWarnings("unchecked")
    private void analyseCycleGroups(ReportContext report, Number internalPackages, String buildUnitName) {
        XsdCycleGroups cycleGroups = report.getCycleGroups();
        double cyclicity = 0;
        double biggestCycleGroupSize = 0;
        double cyclicPackages = 0;

        for (XsdCycleGroup group : cycleGroups.getCycleGroup()) {
            if (group.getNamedElementGroup().equals("Physical build unit package")) {
                if (getBuildUnitName(group).equals(buildUnitName)) {
                    int groupSize = group.getCyclePath().size();
                    cyclicPackages += groupSize;
                    cyclicity += groupSize * groupSize;
                    if (groupSize > biggestCycleGroupSize) {
                        biggestCycleGroupSize = groupSize;
                    }
                    handlePackageCycleGroup(group);
                }
            }
        }
        saveMeasure(SonarJMetrics.BIGGEST_CYCLE_GROUP, biggestCycleGroupSize, 0);
        saveMeasure(SonarJMetrics.CYCLICITY, cyclicity, 0);
        saveMeasure(SonarJMetrics.CYCLIC_PACKAGES, cyclicPackages, 0);

        double relativeCyclicity = 100.0 * Math.sqrt(cyclicity) / internalPackages.doubleValue();
        double relativeCyclicPackages = 100.0 * cyclicPackages / internalPackages.doubleValue();

        saveMeasure(SonarJMetrics.RELATIVE_CYCLICITY, relativeCyclicity, 1);
        saveMeasure(SonarJMetrics.INTERNAL_PACKAGES, internalPackages.doubleValue(), 0);
        saveMeasure(SonarJMetrics.CYCLIC_PACKAGES_PERCENT, relativeCyclicPackages, 1);
    }

    @SuppressWarnings("unchecked")
    private void saveViolation(Rule rule, RulePriority priority, String fqName, int line, String msg) {
        Resource javaFile = sensorContext.getResource(new JavaFile(fqName));

        if (javaFile == null) {
            LOG.error("Cannot obtain resource " + fqName);
        } else {
            Violation v = new Violation(rule, javaFile);

            v.setMessage(msg);
            v.setLineId(line);
            v.setPriority(priority);
            sensorContext.saveViolation(v);
        }
    }

    private String getBuildUnitName(XsdCycleGroup group) {
        if (group.getParent().equals("(Default Build Unit)")) {
            return group.getElementScope();
        }
        return group.getParent();
    }

    private String getBuildUnitName(String fqName) {
        String buName = "<UNKNOWN>";

        if (fqName != null) {
            int colonPos = fqName.indexOf("::");

            if (colonPos != -1) {
                buName = fqName.substring(colonPos + 2);
                if (buName.equals("(Default Build Unit)")) {
                    // Compatibility with old SonarJ versions
                    buName = fqName.substring(0, colonPos);
                }
            }
        }
        return buName;
    }

    private static String relativeFileNameToFqName(String fileName) {
        int lastDot = fileName.lastIndexOf('.');

        return fileName.substring(0, lastDot).replace('/', '.');
    }

    private void handlePackageCycleGroup(XsdCycleGroup group) {
        Rule rule = rulesManager.getPluginRule(SonarJPluginBase.PLUGIN_KEY, SonarJPluginBase.CYCLE_GROUP_RULE_KEY);
        ActiveRule activeRule = rulesProfile.getActiveRule(SonarJPluginBase.PLUGIN_KEY,
                SonarJPluginBase.CYCLE_GROUP_RULE_KEY);

        if (rule != null && activeRule != null) {
            for (XsdCyclePath pathElement : group.getCyclePath()) {
                String fqName = pathElement.getParent();
                Resource javaPackage = sensorContext.getResource(new JavaPackage(fqName));

                if (javaPackage == null) {
                    LOG.error("Cannot obtain resource " + fqName);
                } else {
                    Violation v = new Violation(rule, javaPackage);

                    v.setMessage("Package participates in a cycle group");
                    v.setLineId(1);
                    v.setPriority(activeRule.getPriority());
                    sensorContext.saveViolation(v);
                }
            }
        }
    }

    private int handleArchitectureViolations(XsdViolations violations, String buildUnitName) {
        Rule rule = rulesManager.getPluginRule(SonarJPluginBase.PLUGIN_KEY, SonarJPluginBase.ARCH_RULE_KEY);
        ActiveRule activeRule = rulesProfile.getActiveRule(SonarJPluginBase.PLUGIN_KEY,
                SonarJPluginBase.ARCH_RULE_KEY);
        int count = 0;

        for (XsdArchitectureViolation violation : violations.getArchitectureViolations()) {
            String toName = getAttribute(violation.getArchitectureViolation().getAttribute(), "To");
            String toElemType = getAttribute(violation.getArchitectureViolation().getAttribute(), "To element type")
                    .toLowerCase();
            String target = toElemType + ' ' + toName;

            for (XsdTypeRelation rel : violation.getTypeRelation()) {
                String toType = getAttribute(rel.getAttribute(), "To");
                String msg = "Type " + toType + " from " + target + " must not be used from here";
                String bu = getAttribute(rel.getAttribute(), "From build unit");

                bu = getBuildUnitName(bu);
                if (bu.equals(buildUnitName)) {
                    for (XsdPosition pos : rel.getPosition()) {
                        if (rule != null && activeRule != null) {
                            String relFileName = pos.getFile();

                            if (relFileName != null) {
                                String fqName = relativeFileNameToFqName(relFileName);
                                saveViolation(rule, activeRule.getPriority(), fqName,
                                        Integer.valueOf(pos.getLine()), msg);
                            }
                        }
                        count++;
                    }
                }
            }

        }
        if (rule == null) {
            LOG.error("SonarJ architecture rule not found");
        } else if (activeRule == null) {
            LOG.warn("SonarJ architecture rule deactivated");
        }
        return count;
    }

    private String getRuleKey(String attributeGroup) {
        if (attributeGroup.equals("Duplicate code")) {
            return SonarJPluginBase.DUPLICATE_RULE_KEY;
        }
        if (attributeGroup.equals("Workspace")) {
            return SonarJPluginBase.WORKSPACE_RULE_KEY;
        }
        if (attributeGroup.equals("Threshold")) {
            return SonarJPluginBase.THRESHOLD_RULE_KEY;
        }
        return null;
    }

    private void handleWarnings(XsdWarnings warnings, String buildUnitName) {
        for (XsdWarningsByAttributeGroup warningGroup : warnings.getWarningsByAttributeGroup()) {
            String key = getRuleKey(warningGroup.getAttributeGroup());
            if (key == null) {
                continue;
            }
            Rule rule = rulesManager.getPluginRule(SonarJPluginBase.PLUGIN_KEY, key);
            ActiveRule activeRule = rulesProfile.getActiveRule(SonarJPluginBase.PLUGIN_KEY, key);
            if (rule == null) {
                LOG.error("SonarJ threshold rule not found");
                continue;
            }
            if (activeRule == null) {
                LOG.info("SonarJ threshold rule deactivated");
                continue;
            }
            for (XsdWarningsByAttribute warningByAttribute : warningGroup.getWarningsByAttribute()) {
                String attrName = warningByAttribute.getAttributeName();

                for (XsdWarning warning : warningByAttribute.getWarning()) {
                    String msg = attrName + "=" + getAttribute(warning.getAttribute(), "Attribute value");
                    String bu = getAttribute(warning.getAttribute(), "Build unit");

                    bu = getBuildUnitName(bu);
                    if (bu.equals(buildUnitName)) {
                        if (warning.getPosition().size() > 0) {
                            for (XsdPosition pos : warning.getPosition()) {
                                String relFileName = pos.getFile();

                                if (relFileName != null) {
                                    String fqName = relativeFileNameToFqName(relFileName);

                                    saveViolation(rule, activeRule.getPriority(), fqName,
                                            Integer.valueOf(pos.getLine()), msg);
                                }
                            }
                        } else {
                            String elemType = getAttribute(warning.getAttribute(), "Element type");

                            if (elemType.equals("Class file") || elemType.equals("Source file")) {
                                // Attach a violation at line 1
                                String fileName = getAttribute(warning.getAttribute(), "Element");
                                String fqName = fileName.substring(0, fileName.lastIndexOf('.')).replace('/', '.');

                                saveViolation(rule, activeRule.getPriority(), fqName, 1, msg);
                            }
                        }
                    }
                }
            }
        }
    }

    private String handleDescription(String descr) {
        if (descr.startsWith("Fix warning")) {
            // TODO: handle ascending metrics correctly (99% are descending)
            return "Reduce" + descr.substring(descr.indexOf(':') + 1).toLowerCase();
        }
        if (descr.startsWith("Cut type")) {
            String toType = descr.substring(descr.indexOf("to "));

            return "Cut dependency " + toType;
        }
        if (descr.startsWith("Move type")) {
            String to = descr.substring(descr.indexOf("to "));

            return "Move " + to;
        }
        return descr;
    }

    private int handleTasks(XsdTasks tasks, String buildUnitName) {
        Map<String, RulePriority> priorityMap = new HashMap<String, RulePriority>();

        Rule rule = rulesManager.getPluginRule(SonarJPluginBase.PLUGIN_KEY, SonarJPluginBase.TASK_RULE_KEY);
        int count = 0;

        if (rule == null) {
            LOG.error("SonarJ task rule not found");
            return 0;
        }

        ActiveRule activeRule = rulesProfile.getActiveRule(SonarJPluginBase.PLUGIN_KEY,
                SonarJPluginBase.TASK_RULE_KEY);

        if (activeRule == null) {
            LOG.info("SonarJ task rule not activated");
        }

        priorityMap.put("Low", RulePriority.INFO);
        priorityMap.put("Medium", RulePriority.MINOR);
        priorityMap.put("High", RulePriority.MAJOR);

        for (XsdTask task : tasks.getTask()) {
            String bu = getAttribute(task.getAttribute(), "Build unit");

            bu = getBuildUnitName(bu);
            if (bu.equals(buildUnitName)) {
                String priority = getAttribute(task.getAttribute(), "Priority");
                String description = getAttribute(task.getAttribute(), "Description");
                String assignedTo = getAttribute(task.getAttribute(), "Assigned to");

                description = handleDescription(description); // This should not be needed, but the current description sucks

                int index = description.indexOf(" package");

                if (index > 0 && index < 8) {
                    // Package refactorings won't get markers - this would create to many non relevant markers
                    count++;
                } else {
                    if (assignedTo != null) {
                        assignedTo = '[' + StringUtils.trim(assignedTo) + ']';
                        if (assignedTo.length() > 2) {
                            description += ' ' + assignedTo;
                        }
                    }
                    for (XsdPosition pos : task.getPosition()) {
                        String relFileName = pos.getFile();

                        if (relFileName != null) {
                            String fqName = relativeFileNameToFqName(relFileName);
                            int line = Integer.valueOf(pos.getLine());

                            if (line == 0) {
                                line = 1;
                            }
                            if (activeRule != null) {
                                saveViolation(rule, priorityMap.get(priority), fqName, line, description);
                            }
                        }
                        count++;
                    }
                }
            }
        }
        return count;
    }

    private void addArchitectureMeasures(ReportContext report, String buildUnitName) {
        double types = saveMeasure(INTERNAL_TYPES, SonarJMetrics.INTERNAL_TYPES, 0).getValue();
        Measure unassignedTypes = saveMeasure(UNASSIGNED_TYPES, SonarJMetrics.UNASSIGNED_TYPES, 0);
        Measure violatingTypes = saveMeasure(VIOLATING_TYPES, SonarJMetrics.VIOLATING_TYPES, 0);
        saveMeasure(VIOLATING_DEPENDENCIES, SonarJMetrics.VIOLATING_DEPENDENCIES, 0);
        saveMeasure(TASKS, SonarJMetrics.TASKS, 0);
        saveMeasure(THRESHOLD_WARNINGS, SonarJMetrics.THRESHOLD_WARNINGS, 0);
        saveMeasure(WORKSPACE_WARNINGS, SonarJMetrics.WORKSPACE_WARNINGS, 0);
        saveMeasure(IGNORED_VIOLATIONS, SonarJMetrics.IGNORED_VIOLATONS, 0);
        saveMeasure(IGNORED_WARNINGS, SonarJMetrics.IGNORED_WARNINGS, 0);
        saveMeasure(DUPLICATE_WARNINGS, SonarJMetrics.DUPLICATE_WARNINGS, 0);
        saveMeasure(CYCLE_WARNINGS, SonarJMetrics.CYCLE_WARNINGS, 0);
        saveMeasure(ALL_WARNINGS, SonarJMetrics.ALL_WARNINGS, 0);

        assert types >= 1.0 : "Project must not be empty !";

        double violatingTypesPercent = 100.0 * violatingTypes.getValue() / types;
        double unassignedTypesPercent = 100.0 * unassignedTypes.getValue() / types;

        saveMeasure(SonarJMetrics.VIOLATING_TYPES_PERCENT, violatingTypesPercent, 1);
        saveMeasure(SonarJMetrics.UNASSIGNED_TYPES_PERCENT, unassignedTypesPercent, 1);

        XsdViolations violations = report.getViolations();

        double violating_refs = 0;
        double task_refs = 0;

        if (rulesManager != null && rulesProfile != null) {
            violating_refs = handleArchitectureViolations(violations, buildUnitName);
            handleWarnings(report.getWarnings(), buildUnitName);
            task_refs = handleTasks(report.getTasks(), buildUnitName);
        }
        saveMeasure(SonarJMetrics.ARCHITECTURE_VIOLATIONS, violating_refs, 0);
        saveMeasure(SonarJMetrics.TASK_REFS, task_refs, 0);
    }

    private void analyse(IProject project, XsdAttributeRoot xsdBuildUnit, String buildUnitName,
            ReportContext report, boolean isMultiModuleProject) {
        LOG.info("Adding measures for " + project.getName());

        projectMetrics = readAttributes(xsdBuildUnit);

        Number internalPackages = projectMetrics.get(INTERNAL_PACKAGES);

        if (internalPackages.intValue() == 0) {
            LOG.warn("No classes found in project " + project.getName());
            return;
        }

        double acd = projectMetrics.get(ACD).doubleValue();
        double nccd = projectMetrics.get(NCCD).doubleValue();

        saveMeasure(ACD, SonarJMetrics.ACD, 1);
        saveMeasure(NCCD, SonarJMetrics.NCCD, 1);
        saveMeasure(INSTRUCTIONS, SonarJMetrics.INSTRUCTIONS, 0);
        saveMeasure(JAVA_FILES, SonarJMetrics.JAVA_FILES, 0);
        saveMeasure(TYPE_DEPENDENCIES, SonarJMetrics.TYPE_DEPENDENCIES, 0);
        saveMeasure(EROSION_REFS, SonarJMetrics.EROSION_REFS, 0);
        saveMeasure(EROSION_TYPES, SonarJMetrics.EROSION_TYPES, 0);
        Number structuralDebtIndex = saveMeasure(STUCTURAL_DEBT_INDEX, SonarJMetrics.EROSION_INDEX, 0).getValue();

        if (indexCost > 0) {
            double structuralDebtCost = structuralDebtIndex.doubleValue() * indexCost;
            saveMeasure(SonarJMetrics.EROSION_COST, structuralDebtCost, 0);
        }

        analyseCycleGroups(report, internalPackages, buildUnitName);
        if (projectMetrics.get(UNASSIGNED_TYPES) != null) {
            LOG.info("Adding architecture measures for " + project.getName());
            addArchitectureMeasures(report, buildUnitName);
        }

        AlertDecorator.setAlertLevels(new SensorProjectContext(sensorContext));
    }

    protected void analyse(IProject project, SensorContext sensorContext, ReportContext report) {
        this.sensorContext = sensorContext;

        XsdBuildUnits buildUnits = report.getBuildUnits();
        List<XsdAttributeRoot> buildUnitList = buildUnits.getBuildUnit();

        if (buildUnitList.size() > 1) {
            String longName = project.getArtifactId() + "[" + project.getGroupId() + "]";
            String longName2 = project.getGroupId() + ':' + project.getArtifactId();

            for (XsdAttributeRoot sonarBuildUnit : buildUnitList) {
                String buName = sonarBuildUnit.getName();

                buName = getBuildUnitName(buName);
                if (buName.equals(project.getArtifactId()) || buName.equals(longName) || buName.equals(longName2)) {
                    analyse(project, sonarBuildUnit, buName, report, true);
                    break;
                } else {
                    if (buName.startsWith("...") && longName2.endsWith(buName.substring(2))) {
                        analyse(project, sonarBuildUnit, buName, report, true);
                        break;
                    }
                }
            }
        } else {
            String buName = buildUnitList.get(0).getName();

            buName = getBuildUnitName(buName);
            analyse(project, buildUnitList.get(0), buName, report, false);
        }
    }

    public void analyse(Project project, SensorContext sensorContext) {

        LOG.info("------------------------------------------------------------------------");
        LOG.info("Execute sonar-sonarj-plugin for " + project.getName());
        LOG.info("------------------------------------------------------------------------");
        ReportContext report = readSonarjReport(getReportFileName(project), project.getPackaging());

        if (report != null) {
            analyse(new ProjectDelegate(project), sensorContext, report);
        }
    }

    public final String getReportFileName(Project project) {
        return project.getFileSystem().getBuildDir().getPath() + '/' + REPORT_DIR + '/' + REPORT_NAME;
    }
}