/** * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ package net.sourceforge.pmd.lang.ecmascript.ast; import java.io.IOException; import java.io.Reader; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.commons.io.IOUtils; import org.mozilla.javascript.CompilerEnvirons; import org.mozilla.javascript.Parser; import org.mozilla.javascript.ast.AstRoot; import org.mozilla.javascript.ast.Comment; import org.mozilla.javascript.ast.ErrorCollector; import org.mozilla.javascript.ast.ParseProblem; import net.sourceforge.pmd.lang.ast.ParseException; import net.sourceforge.pmd.lang.ecmascript.EcmascriptParserOptions; public class EcmascriptParser { protected final EcmascriptParserOptions parserOptions; private Map<Integer, String> suppressMap; private String suppressMarker = "NOPMD"; // that's the default value public EcmascriptParser(EcmascriptParserOptions parserOptions) { this.parserOptions = parserOptions; if (parserOptions.getSuppressMarker() != null) { suppressMarker = parserOptions.getSuppressMarker(); } } protected AstRoot parseEcmascript(final String sourceCode, final List<ParseProblem> parseProblems) throws ParseException { final CompilerEnvirons compilerEnvirons = new CompilerEnvirons(); compilerEnvirons.setRecordingComments(parserOptions.isRecordingComments()); compilerEnvirons.setRecordingLocalJsDocComments(parserOptions.isRecordingLocalJsDocComments()); compilerEnvirons.setLanguageVersion(parserOptions.getRhinoLanguageVersion().getVersion()); // Scope's don't appear to get set right without this compilerEnvirons.setIdeMode(true); compilerEnvirons.setWarnTrailingComma(true); // see bug #1150 "EmptyExpression" for valid statements! compilerEnvirons.setReservedKeywordAsIdentifier(true); // TODO We should do something with Rhino errors... final ErrorCollector errorCollector = new ErrorCollector(); final Parser parser = new Parser(compilerEnvirons, errorCollector); // TODO Fix hardcode final String sourceURI = "unknown"; final int beginLineno = 1; AstRoot astRoot = parser.parse(sourceCode, sourceURI, beginLineno); parseProblems.addAll(errorCollector.getErrors()); return astRoot; } public EcmascriptNode<AstRoot> parse(final Reader reader) { try { final List<ParseProblem> parseProblems = new ArrayList<>(); final String sourceCode = IOUtils.toString(reader); final AstRoot astRoot = parseEcmascript(sourceCode, parseProblems); final EcmascriptTreeBuilder treeBuilder = new EcmascriptTreeBuilder(sourceCode, parseProblems); EcmascriptNode<AstRoot> tree = treeBuilder.build(astRoot); suppressMap = new HashMap<>(); if (astRoot.getComments() != null) { for (Comment comment : astRoot.getComments()) { int nopmd = comment.getValue().indexOf(suppressMarker); if (nopmd > -1) { String suppression = comment.getValue().substring(nopmd + suppressMarker.length()); EcmascriptNode<Comment> node = treeBuilder.build(comment); suppressMap.put(node.getBeginLine(), suppression); } } } return tree; } catch (IOException e) { throw new ParseException(e); } } public Map<Integer, String> getSuppressMap() { return suppressMap; } }