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.checkstyle;
021
022import com.google.common.annotations.VisibleForTesting;
023import com.puppycrawl.tools.checkstyle.api.AuditEvent;
024import com.puppycrawl.tools.checkstyle.api.AuditListener;
025import org.apache.commons.lang.StringUtils;
026import org.slf4j.Logger;
027import org.slf4j.LoggerFactory;
028import org.sonar.api.BatchExtension;
029import org.sonar.api.batch.SensorContext;
030import org.sonar.api.resources.JavaFile;
031import org.sonar.api.resources.Project;
032import org.sonar.api.resources.Resource;
033import org.sonar.api.rules.Rule;
034import org.sonar.api.rules.RuleFinder;
035import org.sonar.api.rules.Violation;
036
037/**
038 * @since 2.3
039 */
040public class CheckstyleAuditListener implements AuditListener, BatchExtension {
041
042  private static final Logger LOG = LoggerFactory.getLogger(CheckstyleAuditListener.class);
043
044  private final SensorContext context;
045  private final Project project;
046  private final RuleFinder ruleFinder;
047  private Resource currentResource = null;
048
049  public CheckstyleAuditListener(SensorContext context, Project project, RuleFinder ruleFinder) {
050    this.context = context;
051    this.project = project;
052    this.ruleFinder = ruleFinder;
053  }
054
055  public void auditStarted(AuditEvent event) {
056    // nop
057  }
058
059  public void auditFinished(AuditEvent event) {
060    // nop
061  }
062
063  public void fileStarted(AuditEvent event) {
064    // nop
065  }
066
067  public void fileFinished(AuditEvent event) {
068    currentResource = null;
069  }
070
071  public void addError(AuditEvent event) {
072    String ruleKey = getRuleKey(event);
073    if (ruleKey != null) {
074      String message = getMessage(event);
075      // In Checkstyle 5.5 exceptions are reported as an events from TreeWalker
076      if ("com.puppycrawl.tools.checkstyle.TreeWalker".equals(ruleKey)) {
077        LOG.warn(message);
078      }
079      Rule rule = ruleFinder.findByKey(CheckstyleConstants.REPOSITORY_KEY, ruleKey);
080      if (rule != null) {
081        initResource(event);
082        Violation violation = Violation.create(rule, currentResource)
083            .setLineId(getLineId(event))
084            .setMessage(message);
085        context.saveViolation(violation);
086      }
087    }
088  }
089
090  private void initResource(AuditEvent event) {
091    if (currentResource == null) {
092      String absoluteFilename = event.getFileName();
093      currentResource = JavaFile.fromAbsolutePath(absoluteFilename, project.getFileSystem().getSourceDirs(), false);
094    }
095  }
096
097  @VisibleForTesting
098  static String getRuleKey(AuditEvent event) {
099    String key = null;
100    try {
101      key = event.getModuleId();
102    } catch (Exception e) {
103      // checkstyle throws a NullPointerException if the message is not set
104    }
105    if (StringUtils.isBlank(key)) {
106      try {
107        key = event.getSourceName();
108      } catch (Exception e) {
109        // checkstyle can throw a NullPointerException if the message is not set
110      }
111    }
112    return key;
113  }
114
115  @VisibleForTesting
116  static String getMessage(AuditEvent event) {
117    try {
118      return event.getMessage();
119
120    } catch (Exception e) {
121      // checkstyle can throw a NullPointerException if the message is not set
122      return null;
123    }
124  }
125
126  @VisibleForTesting
127  static Integer getLineId(AuditEvent event) {
128    try {
129      int line = event.getLine();
130      // checkstyle returns 0 if there is no relation to a file content, but we use null
131      return line == 0 ? null : line;
132
133    } catch (Exception e) {
134      // checkstyle can throw a NullPointerException if the message is not set
135      return null;
136    }
137  }
138
139  /**
140   * Note that this method never invoked from Checkstyle 5.5.
141   */
142  public void addException(AuditEvent event, Throwable throwable) {
143    // nop
144  }
145
146  Resource getCurrentResource() {
147    return currentResource;
148  }
149}