All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.sonar.java.ast.JavaAstScanner Maven / Gradle / Ivy

The newest version!
/*
 * Sonar, open source software quality management tool.
 * Copyright (C) 2008-2012 SonarSource
 * mailto:contact AT sonarsource DOT com
 *
 * Sonar is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 3 of the License, or (at your option) any later version.
 *
 * Sonar is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with Sonar; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
 */
package org.sonar.java.ast;

import com.google.common.collect.Lists;
import com.google.common.io.Closeables;
import com.puppycrawl.tools.checkstyle.Checker;
import com.puppycrawl.tools.checkstyle.ConfigurationLoader;
import com.puppycrawl.tools.checkstyle.PropertiesExpander;
import com.puppycrawl.tools.checkstyle.api.Configuration;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.filefilter.FileFilterUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sonar.api.resources.InputFile;
import org.sonar.api.resources.InputFileUtils;
import org.sonar.java.ast.visitor.*;
import org.sonar.java.squid.JavaSquidConfiguration;
import org.sonar.squid.api.AnalysisException;
import org.sonar.squid.api.CodeScanner;
import org.sonar.squid.api.CodeVisitor;
import org.sonar.squid.api.SourceCode;
import org.xml.sax.InputSource;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Stack;

/**
 * Squid uses Checkstyle to get an out-of-the-box java parser with AST generation and visitor pattern support.
 */
public class JavaAstScanner extends CodeScanner {

  private static final Logger LOG = LoggerFactory.getLogger(JavaAstScanner.class);
  private JavaSquidConfiguration conf;
  private SourceCode project;

  public JavaAstScanner(JavaSquidConfiguration conf, SourceCode project) {
    this.conf = conf;
    this.project = project;
  }

  public JavaAstScanner scanDirectory(File javaSourceDirectory) {
    List inputFiles = Lists.newArrayList();
    Collection files = FileUtils.listFiles(javaSourceDirectory, FileFilterUtils.fileFileFilter(), FileFilterUtils.directoryFileFilter());
    for (File file : files) {
      inputFiles.add(InputFileUtils.create(javaSourceDirectory, file));
    }
    return scanFiles(inputFiles);
  }

  public JavaAstScanner scanFile(InputFile javaFile) {
    return scanFiles(Arrays.asList(javaFile));
  }

  public JavaAstScanner scanFiles(Collection inputFiles) {
    if (LOG.isDebugEnabled()) {
      LOG.debug("----- Java sources analyzed by Squid:");
      for (InputFile inputFile : inputFiles) {
        LOG.debug(inputFile.toString());
      }
      LOG.debug("-----");
    }

    Stack resourcesStack = new Stack();
    resourcesStack.add(project);
    for (JavaAstVisitor visitor : getVisitors()) {
      visitor.setSourceCodeStack(resourcesStack);
    }

    CheckstyleSquidBridgeContext bridgeContext = new CheckstyleSquidBridgeContext()
        .setASTVisitors(getVisitors())
        .setSquidConfiguration(conf)
        .setInputFiles(inputFiles);

    CheckstyleSquidBridge.setContext(bridgeContext);
    try {
      launchCheckstyle(InputFileUtils.toFiles(inputFiles), conf.getCharset());
    } finally {
      // Garbage collector should be able to do his job, so we must clean context after execution
      CheckstyleSquidBridge.setContext(null);
    }
    return this;
  }

  private void launchCheckstyle(Collection files, Charset charset) {
    Checker c = createChecker(charset);
    ClassLoader initialClassLoader = Thread.currentThread().getContextClassLoader();
    Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
    try {
      c.setClassloader(getClass().getClassLoader());
      c.setModuleClassLoader(getClass().getClassLoader());
      c.process(Lists. newArrayList(files));
      c.destroy();
    } finally {
      Thread.currentThread().setContextClassLoader(initialClassLoader);
    }
  }

  private Checker createChecker(Charset charset) {
    String checkstyleConfig = loadCheckstyleConfigToString()
        .replace("${charset}", charset.toString());
    try {
      Configuration config = ConfigurationLoader.loadConfiguration(
          new InputSource(new ByteArrayInputStream(checkstyleConfig.getBytes())),
          new PropertiesExpander(System.getProperties()),
          false);
      Checker checker = new Checker();
      final ClassLoader moduleClassLoader = Checker.class.getClassLoader();
      checker.setModuleClassLoader(moduleClassLoader);
      checker.configure(config);
      checker.addListener(new CheckstyleAuditListener());
      return checker;
    } catch (Exception e) { // NOSONAR We want to be sure to catch any unexpected exception
      throw new AnalysisException("Unable to create Checkstyle Checker", e);
    }
  }

  private static String loadCheckstyleConfigToString() {
    InputStream is = null;
    try {
      is = JavaAstScanner.class.getClassLoader().getResourceAsStream("checkstyle-configuration.xml");
      return IOUtils.toString(is);
    } catch (IOException e) {
      throw new AnalysisException("Unable to load Checkstyle configuration for Java Squid", e);
    } finally {
      Closeables.closeQuietly(is);
    }
  }

  @Override
  public Collection> getVisitorClasses() {
    List> visitorClasses = Lists.newArrayList();
    visitorClasses.add(PackageVisitor.class);
    visitorClasses.add(FileVisitor.class);
    visitorClasses.add(ClassVisitor.class);
    visitorClasses.add(AnonymousInnerClassVisitor.class);
    visitorClasses.add(MethodVisitor.class);
    visitorClasses.add(EndAtLineVisitor.class);
    visitorClasses.add(LinesVisitor.class);
    visitorClasses.add(BlankLinesVisitor.class);
    visitorClasses.add(CommentVisitor.class);
    visitorClasses.add(PublicApiVisitor.class);
    visitorClasses.add(BranchVisitor.class);
    visitorClasses.add(StatementVisitor.class);
    if (conf.isAnalysePropertyAccessors()) {
      visitorClasses.add(AccessorVisitor.class);
    }
    visitorClasses.add(ComplexityVisitor.class);
    visitorClasses.add(LinesOfCodeVisitor.class);
    visitorClasses.add(FileLinesVisitor.class);
    return visitorClasses;
  }

  @Override
  public void accept(CodeVisitor visitor) {
    if (visitor instanceof JavaAstVisitor) {
      super.accept(visitor);
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy