| /** |
| * Validator for Closure-based JSDoc. |
| */ |
| |
| package org.chromium.devtools.jsdoc; |
| |
| import java.io.IOException; |
| import java.nio.charset.Charset; |
| import java.nio.file.FileSystems; |
| import java.nio.file.Files; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.SortedSet; |
| import java.util.concurrent.ExecutionException; |
| import java.util.concurrent.ExecutorService; |
| import java.util.concurrent.Executors; |
| import java.util.concurrent.Future; |
| |
| public class JsDocValidator { |
| private static final String FILES_LIST_NAME = "--files-list-name"; |
| |
| private void run(String[] args) { |
| if (args.length == 0) { |
| System.err.println("At least 1 argument is expected"); |
| System.exit(1); |
| } |
| String[] files; |
| if (FILES_LIST_NAME.equals(args[0])) { |
| files = readFileNames(args); |
| } else { |
| files = args; |
| } |
| int threadCount = Math.min(files.length, Runtime.getRuntime().availableProcessors()); |
| ExecutorService executor = Executors.newFixedThreadPool(threadCount); |
| try { |
| runWithExecutor(files, executor); |
| } finally { |
| executor.shutdown(); |
| } |
| } |
| |
| private String[] readFileNames(String[] args) { |
| if (args.length != 2) { |
| System.err.println("A single file name is expected to follow " + FILES_LIST_NAME); |
| System.exit(1); |
| } |
| try { |
| List<String> list = Files.readAllLines( |
| FileSystems.getDefault().getPath(args[1]), Charset.forName("UTF-8")); |
| return list.toArray(new String[list.size()]); |
| } catch (IOException e) { |
| System.err.println("Unable to read list file " + args[1]); |
| e.printStackTrace(); |
| System.exit(1); |
| return new String[0]; |
| } |
| } |
| |
| private void runWithExecutor(String[] args, ExecutorService executor) { |
| List<Future<ValidatorContext>> futures = new ArrayList<>(args.length); |
| Map<Future<ValidatorContext>, String> fileNamesByFuture = new HashMap<>(); |
| for (String fileName : args) { |
| Future<ValidatorContext> future = executor.submit(new FileCheckerCallable(fileName)); |
| fileNamesByFuture.put(future, fileName); |
| futures.add(future); |
| } |
| |
| List<ValidatorContext> contexts = new ArrayList<>(args.length); |
| for (Future<ValidatorContext> future : futures) { |
| try { |
| ValidatorContext context = future.get(); |
| if (context != null) { |
| contexts.add(context); |
| } |
| } catch (InterruptedException | ExecutionException e) { |
| if (e.getMessage().indexOf("DESTRUCTURING_LHS") == -1) { |
| System.err.println( |
| fileNamesByFuture.get(future) + ": ERROR - " + e.getMessage()); |
| } |
| } |
| } |
| |
| int entryCount = 0; |
| for (ValidatorContext context : contexts) { |
| entryCount += context.getValidationResult().size(); |
| } |
| List<LogEntry> entries = new ArrayList<>(entryCount); |
| for (ValidatorContext context : contexts) { |
| SortedSet<ValidatorContext.MessageRecord> records = context.getValidationResult(); |
| for (ValidatorContext.MessageRecord record : records) { |
| entries.add(new LogEntry(context.scriptFileName, record)); |
| } |
| } |
| Collections.sort(entries); |
| for (LogEntry entry : entries) { |
| System.err.println(entry.record.text); |
| } |
| if (!entries.isEmpty()) System.err.println("Total errors: " + entries.size()); |
| } |
| |
| public static void main(String[] args) { |
| new JsDocValidator().run(args); |
| } |
| |
| private static class LogEntry implements Comparable<LogEntry> { |
| private final String fileName; |
| private final ValidatorContext.MessageRecord record; |
| |
| LogEntry(String fileName, ValidatorContext.MessageRecord record) { |
| this.fileName = fileName; |
| this.record = record; |
| } |
| |
| @Override |
| public int compareTo(LogEntry other) { |
| int result = fileName.compareTo(other.fileName); |
| if (result != 0) { |
| return result; |
| } |
| return Integer.compare(record.position, other.record.position); |
| } |
| |
| @Override |
| public int hashCode() { |
| return 17 + fileName.hashCode() * 3 + this.record.hashCode() * 5; |
| } |
| |
| @Override |
| public boolean equals(Object obj) { |
| if (this == obj) { |
| return true; |
| } |
| if (obj == null) { |
| return false; |
| } |
| if (getClass() != obj.getClass()) { |
| return false; |
| } |
| LogEntry other = (LogEntry) obj; |
| if (fileName != other.fileName |
| && (fileName != null && !fileName.equals(other.fileName))) { |
| return false; |
| } |
| |
| if (record == other.record) { |
| return true; |
| } |
| if (record != null) { |
| if (other.record == null) { |
| return false; |
| } |
| return record.position == other.record.position; |
| } |
| return false; |
| } |
| } |
| } |