package pl.touk.sputnik.processor.jslint;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import pl.touk.sputnik.configuration.Configuration;
import pl.touk.sputnik.configuration.GeneralOption;
import pl.touk.sputnik.review.Review;
import pl.touk.sputnik.review.ReviewException;
import pl.touk.sputnik.review.ReviewProcessor;
import pl.touk.sputnik.review.ReviewResult;
import pl.touk.sputnik.review.Severity;
import pl.touk.sputnik.review.Violation;
import pl.touk.sputnik.review.filter.JavaScriptFilter;
import pl.touk.sputnik.review.transformer.IOFileTransformer;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import com.google.common.base.Strings;
import com.googlecode.jslint4java.Issue;
import com.googlecode.jslint4java.JSLint;
import com.googlecode.jslint4java.JSLintBuilder;
import com.googlecode.jslint4java.JSLintResult;
import com.googlecode.jslint4java.Option;
@Slf4j
@AllArgsConstructor
public class JsLintProcessor implements ReviewProcessor {
private static final String SOURCE_NAME = "JSLint";
@NotNull
private final Configuration config;
@Override
public ReviewResult process(Review review) {
final Properties configProperties = loadConfigurationProperties();
return toReviewResult(lint(review, configProperties));
}
private ReviewResult toReviewResult(List<Issue> lintIssues) {
ReviewResult result = new ReviewResult();
for (Issue issue : lintIssues) {
result.add(new Violation(issue.getSystemId(), issue.getLine(), issue.getReason(), Severity.INFO));
}
return result;
}
private List<Issue> lint(Review review, Properties configProperties) {
JSLint jsLint = new JSLintBuilder().fromDefault();
applyProperties(jsLint, configProperties);
List<Issue> lintIssues = new ArrayList<>();
List<File> files = review.getFiles(new JavaScriptFilter(), new IOFileTransformer());
for (File file : files) {
lintIssues.addAll(lintFile(jsLint, file));
}
return lintIssues;
}
private void applyProperties(JSLint jsLint, Properties configProperties) {
if (configProperties == null) {
return;
}
for (String propertyName : configProperties.stringPropertyNames()) {
Option option;
try {
option = Option.valueOf(propertyName.toUpperCase());
} catch (IllegalArgumentException e) {
log.warn("Unknown JSLint configuration property '{}'. Continue.", propertyName);
continue;
}
jsLint.addOption(option, configProperties.getProperty(propertyName));
}
}
private List<Issue> lintFile(JSLint jsLint, File file) {
try (FileReader fileReader = new FileReader(file)) {
JSLintResult lintResult = jsLint.lint(file.getAbsolutePath(), fileReader);
return lintResult.getIssues();
} catch (IOException e) {
throw new ReviewException("IO exception when running JSLint.", e);
}
}
/**
* Load JSLint configuration property file specified by {@link GeneralOption#JSLINT_CONFIGURATION_FILE}
* configuration key
*
* @return a Properties instance
*/
private Properties loadConfigurationProperties() {
String configurationFileName = getConfigurationFileName();
if (Strings.isNullOrEmpty(configurationFileName)) {
log.info("JSLint property file not specified. Using default configuration.");
return null;
}
return loadProperties(configurationFileName);
}
private Properties loadProperties(String configurationFileName) {
final Properties props = new Properties();
try (FileReader fileReader = new FileReader(configurationFileName)) {
log.info("Loading {}", configurationFileName);
props.load(fileReader);
} catch (IOException e) {
log.error("Load JSLint properties operation failed", e);
throw new ReviewException("IO exception when reading JSLint configuration file.", e);
}
return props;
}
private String getConfigurationFileName() {
String configurationFile = config.getProperty(GeneralOption.JSLINT_CONFIGURATION_FILE);
log.info("Using JSLint configuration file {}", configurationFile);
return configurationFile;
}
@Override
public String getName() {
return SOURCE_NAME;
}
}