1package org.chromium.devtools.jsdoc;
2
3import com.google.javascript.rhino.head.CompilerEnvirons;
4import com.google.javascript.rhino.head.IRFactory;
5import com.google.javascript.rhino.head.ast.AstNode;
6import com.google.javascript.rhino.head.ast.AstRoot;
7
8import org.chromium.devtools.jsdoc.checks.ContextTrackingValidationCheck;
9
10import java.io.FileNotFoundException;
11import java.io.IOException;
12import java.io.StringReader;
13import java.nio.ByteBuffer;
14import java.nio.charset.StandardCharsets;
15import java.nio.file.FileSystems;
16import java.nio.file.Files;
17import java.util.ArrayList;
18import java.util.List;
19import java.util.concurrent.Callable;
20
21public class FileCheckerCallable implements Callable<ValidatorContext> {
22
23    private final String fileName;
24
25    public FileCheckerCallable(String fileName) {
26        this.fileName = fileName;
27    }
28
29    @Override
30    public ValidatorContext call() {
31        try {
32            ValidatorContext context = new ValidatorContext(readScriptText(), fileName);
33            AstRoot node = parseScript(context);
34            ValidationCheckDispatcher dispatcher = new ValidationCheckDispatcher(context);
35            dispatcher.registerCheck(new ContextTrackingValidationCheck());
36            node.visit(dispatcher);
37            dispatcher.flush();
38            return context;
39        } catch (FileNotFoundException e) {
40            logError("File not found: " + fileName);
41        } catch (IOException e) {
42            logError("Failed to read file " + fileName);
43        }
44        return null;
45    }
46
47    private ScriptText readScriptText() throws IOException {
48        byte[] encoded = Files.readAllBytes(FileSystems.getDefault().getPath(fileName));
49        String text = StandardCharsets.UTF_8.decode(ByteBuffer.wrap(encoded)).toString();
50        return new ScriptText(text);
51    }
52
53    private static AstRoot parseScript(ValidatorContext context) throws IOException {
54        CompilerEnvirons env = new CompilerEnvirons();
55        env.setRecoverFromErrors(true);
56        env.setGenerateDebugInfo(true);
57        env.setRecordingLocalJsDocComments(true);
58        env.setAllowSharpComments(true);
59        env.setRecordingComments(true);
60        IRFactory factory = new IRFactory(env);
61        return factory.parse(new StringReader(context.scriptText.text), context.scriptFileName, 1);
62    }
63
64    private static void logError(String message) {
65        System.err.println("ERROR: " + message);
66    }
67
68    private static class ValidationCheckDispatcher extends DoDidVisitorAdapter {
69        private final List<ValidationCheck> checks = new ArrayList<>(2);
70        private final ValidatorContext context;
71
72        public ValidationCheckDispatcher(ValidatorContext context) {
73            this.context = context;
74        }
75
76        public void registerCheck(ValidationCheck check) {
77            check.setContext(context);
78            checks.add(check);
79        }
80
81        @Override
82        public void doVisit(AstNode node) {
83            for (DoDidNodeVisitor visitor : checks) {
84                visitor.doVisit(node);
85            }
86        }
87
88        @Override
89        public void didVisit(AstNode node) {
90            for (ValidationCheck check : checks) {
91                check.didVisit(node);
92            }
93        }
94    }
95}
96