1package org.chromium.devtools.jsdoc;
2
3import com.google.javascript.jscomp.SourceFile;
4import com.google.javascript.rhino.Node;
5
6import java.io.IOException;
7import java.util.Collections;
8import java.util.Comparator;
9import java.util.SortedSet;
10import java.util.TreeSet;
11
12public class ValidatorContext {
13
14    private static final Comparator<MessageRecord> MESSAGE_RECORD_COMPARATOR =
15            new Comparator<MessageRecord>() {
16                @Override
17                public int compare(MessageRecord left, MessageRecord right) {
18                    return left.position - right.position;
19                }
20            };
21
22    public final String scriptFileName;
23    public final SourceFile sourceFile;
24    private final SortedSet<MessageRecord> validationResult =
25            new TreeSet<>(MESSAGE_RECORD_COMPARATOR);
26
27
28    public ValidatorContext(String text, String scriptFileName) {
29        this.scriptFileName = scriptFileName;
30        this.sourceFile = SourceFile.builder().buildFromCode(scriptFileName, text);
31    }
32
33    public SortedSet<MessageRecord> getValidationResult() {
34        return Collections.unmodifiableSortedSet(validationResult);
35    }
36
37    public String getNodeText(Node node) {
38        if (node == null) {
39            return null;
40        }
41        try {
42            return sourceFile.getCode().substring(
43                    node.getSourceOffset(), node.getSourceOffset() + node.getLength());
44        } catch (IOException e) {
45            return null;
46        }
47    }
48
49    public SourcePosition getPosition(int offset) {
50        return new SourcePosition(
51                sourceFile.getLineOfOffset(offset), sourceFile.getColumnOfOffset(offset));
52    }
53
54    public void reportErrorInNode(Node node, int offsetInNodeText, String errorText) {
55        int errorAbsoluteOffset = node.getSourceOffset() + offsetInNodeText;
56        reportErrorAtOffset(errorAbsoluteOffset, errorText);
57    }
58
59    public void reportErrorAtOffset(int offset, String errorText) {
60        SourcePosition position = getPosition(offset);
61        StringBuilder positionMarker = new StringBuilder(position.column + 1);
62        for (int i = position.column; i > 0; --i) {
63            positionMarker.append(' ');
64        }
65        positionMarker.append('^');
66        String message = String.format("%s:%d: ERROR - %s%n%s%n%s%n",
67                scriptFileName,
68                position.line,
69                errorText,
70                sourceFile.getLine(position.line),
71                positionMarker.toString());
72        validationResult.add(new MessageRecord(offset, message));
73    }
74
75    public static class MessageRecord {
76        public final int position;
77        public final String text;
78
79        public MessageRecord(int position, String text) {
80            this.position = position;
81            this.text = text;
82        }
83    }
84
85    public static class SourcePosition {
86        public final int line;
87        public final int column;
88
89        public SourcePosition(int line, int column) {
90            this.line = line;
91            this.column = column;
92        }
93    }
94}
95