diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -219,6 +219,7 @@ 'ArcanistRuboCopLinterTestCase' => 'lint/linter/__tests__/ArcanistRuboCopLinterTestCase.php', 'ArcanistRubyLinter' => 'lint/linter/ArcanistRubyLinter.php', 'ArcanistRubyLinterTestCase' => 'lint/linter/__tests__/ArcanistRubyLinterTestCase.php', + 'ArcanistScalastyleLinter' => 'lint/linter/ArcanistScalastyleLinter.php', 'ArcanistScriptAndRegexLinter' => 'lint/linter/ArcanistScriptAndRegexLinter.php', 'ArcanistSelfMemberReferenceXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistSelfMemberReferenceXHPASTLinterRule.php', 'ArcanistSemicolonSpacingXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistSemicolonSpacingXHPASTLinterRule.php', @@ -505,6 +506,7 @@ 'ArcanistRuboCopLinterTestCase' => 'ArcanistExternalLinterTestCase', 'ArcanistRubyLinter' => 'ArcanistExternalLinter', 'ArcanistRubyLinterTestCase' => 'ArcanistExternalLinterTestCase', + 'ArcanistScalastyleLinter' => 'ArcanistExternalLinter', 'ArcanistScriptAndRegexLinter' => 'ArcanistLinter', 'ArcanistSelfMemberReferenceXHPASTLinterRule' => 'ArcanistXHPASTLinterRule', 'ArcanistSemicolonSpacingXHPASTLinterRule' => 'ArcanistXHPASTLinterRule', diff --git a/src/lint/linter/ArcanistScalastyleLinter.php b/src/lint/linter/ArcanistScalastyleLinter.php new file mode 100644 --- /dev/null +++ b/src/lint/linter/ArcanistScalastyleLinter.php @@ -0,0 +1,124 @@ +jarPath, + '--config', + $this->configPath, + '--quiet', + 'true', + ); + } + + public function getLinterConfigurationOptions() { + return parent::getLinterConfigurationOptions() + array( + 'scalastyle.config' => array( + 'type' => 'string', + 'help' => pht('Path to the scalastyle-configuration.xml'), + ), + 'scalastyle.jar' => array( + 'type' => 'string', + 'help' => pht('Path to the scalastyle-batch jar file'), + ), + ); + } + + public function setLinterConfigurationValue($key, $value) { + switch ($key) { + case 'scalastyle.config': + $this->configPath = Filesystem::resolvePath($value, + $this->getProjectRoot()); + if (!file_exists($this->configPath)) { + throw new ArcanistMissingLinterException( + pht('Unable to locate scalastyle configuration at "%s"', + $this->configPath)); + } + return; + case 'scalastyle.jar': + $this->jarPath = Filesystem::resolvePath($value, + $this->getProjectRoot()); + if (!file_exists($this->jarPath)) { + throw new ArcanistMissingLinterException( + pht('Unable to locate scalastyle jar at "%s"', + $this->jarPath)); + } + return; + } + + parent::setLinterConfigurationValue($key, $value); + } + + protected function parseLinterOutput($path, $err, $stdout, $stderr) { + $output_pattern = '/^(?Pwarning|error|exception) '. + 'file=(?P.*) message=(?P.*) line=(?P\d+)'. + '(?: column=(?P\d+))?$/'; + $messages = array(); + $lines = explode(PHP_EOL, trim($stdout)); + + foreach ($lines as $line) { + $matches = null; + if (preg_match($output_pattern, $line, $matches)) { + $message = new ArcanistLintMessage(); + $message->setCode($this->getLinterName()); + $message->setSeverity( + $this->mapScalastyleSeverity($matches['severity'])); + $message->setPath($matches['path']); + $message->setDescription($matches['description']); + $message->setLine($matches['line']); + if (array_key_exists('char', $matches)) { + $message->setChar($matches['char']); + } + $messages[] = $message; + } + } + + return $messages; + } + + public function getLinterName() { + return 'scalastyle'; + } + + private function mapScalastyleSeverity($severity) { + switch ($severity) { + case 'warning': + return ArcanistLintSeverity::SEVERITY_WARNING; + case 'error': + return ArcanistLintSeverity::SEVERITY_ERROR; + case 'exception': + return ArcanistLintSeverity::SEVERITY_DISABLED; + } + throw new Exception( + pht('Unrecognized scalastyle severity "%s"', $severity), + $this->getLinterName()); + } +}