| /* |
| * Copyright (c) 2013, the Dart project authors. |
| * |
| * Licensed under the Eclipse Public License v1.0 (the "License"); you may not use this file except |
| * in compliance with the License. You may obtain a copy of the License at |
| * |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Unless required by applicable law or agreed to in writing, software distributed under the License |
| * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
| * or implied. See the License for the specific language governing permissions and limitations under |
| * the License. |
| */ |
| package com.google.dart.engine.html.parser; |
| |
| import com.google.dart.engine.ast.CompilationUnit; |
| import com.google.dart.engine.ast.Expression; |
| import com.google.dart.engine.error.AnalysisErrorListener; |
| import com.google.dart.engine.html.ast.HtmlScriptTagNode; |
| import com.google.dart.engine.html.ast.HtmlUnit; |
| import com.google.dart.engine.html.ast.XmlAttributeNode; |
| import com.google.dart.engine.html.ast.XmlNode; |
| import com.google.dart.engine.html.ast.XmlTagNode; |
| import com.google.dart.engine.html.scanner.Token; |
| import com.google.dart.engine.parser.Parser; |
| import com.google.dart.engine.scanner.Scanner; |
| import com.google.dart.engine.scanner.SubSequenceReader; |
| import com.google.dart.engine.source.Source; |
| import com.google.dart.engine.utilities.source.LineInfo; |
| import com.google.dart.engine.utilities.source.LineInfo.Location; |
| |
| import java.util.Arrays; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Set; |
| |
| /** |
| * Instances of the class {@code HtmlParser} are used to parse tokens into a AST structure comprised |
| * of {@link XmlNode}s. |
| * |
| * @coverage dart.engine.html |
| */ |
| public class HtmlParser extends XmlParser { |
| /** |
| * The line information associated with the source being parsed. |
| */ |
| private LineInfo lineInfo; |
| |
| /** |
| * The error listener to which errors will be reported. |
| */ |
| private AnalysisErrorListener errorListener; |
| |
| private static final String APPLICATION_DART_IN_DOUBLE_QUOTES = "\"application/dart\""; |
| private static final String APPLICATION_DART_IN_SINGLE_QUOTES = "'application/dart'"; |
| private static final String SCRIPT = "script"; |
| private static final String TYPE = "type"; |
| |
| /** |
| * A set containing the names of tags that do not have a closing tag. |
| */ |
| public static Set<String> SELF_CLOSING = new HashSet<String>(Arrays.asList(new String[] { |
| "area", "base", "basefont", "br", "col", "frame", "hr", "img", "input", "link", "meta", |
| "param", "!"})); |
| |
| /** |
| * Given the contents of an embedded expression that occurs at the given offset, parse it as a |
| * Dart expression. The contents should not include the expression's delimiters. |
| * |
| * @param source the source that contains that given token |
| * @param token the token to start parsing from |
| * @return the Dart expression that was parsed |
| */ |
| public static Expression parseEmbeddedExpression(Source source, |
| com.google.dart.engine.scanner.Token token, AnalysisErrorListener errorListener) { |
| Parser parser = new Parser(source, errorListener); |
| return parser.parseExpression(token); |
| } |
| |
| /** |
| * Given the contents of an embedded expression that occurs at the given offset, scans it as a |
| * Dart code. |
| * |
| * @param source the source of that contains the given contents |
| * @param contents the contents to scan |
| * @param contentOffset the offset of the contents in the larger file |
| * @return the first Dart token |
| */ |
| public static com.google.dart.engine.scanner.Token scanDartSource(Source source, |
| LineInfo lineInfo, String contents, int contentOffset, AnalysisErrorListener errorListener) { |
| Location location = lineInfo.getLocation(contentOffset); |
| Scanner scanner = new Scanner( |
| source, |
| new SubSequenceReader(contents, contentOffset), |
| errorListener); |
| scanner.setSourceStart(location.getLineNumber(), location.getColumnNumber()); |
| return scanner.tokenize(); |
| } |
| |
| /** |
| * Construct a parser for the specified source. |
| * |
| * @param source the source being parsed |
| * @param errorListener the error listener to which errors will be reported |
| */ |
| public HtmlParser(Source source, AnalysisErrorListener errorListener) { |
| super(source); |
| this.errorListener = errorListener; |
| } |
| |
| /** |
| * Parse the given tokens. |
| * |
| * @param token the first token in the stream of tokens to be parsed |
| * @param lineInfo the line information created by the scanner |
| * @return the parse result (not {@code null}) |
| */ |
| public HtmlUnit parse(Token token, LineInfo lineInfo) { |
| this.lineInfo = lineInfo; |
| List<XmlTagNode> tagNodes = parseTopTagNodes(token); |
| return new HtmlUnit(token, tagNodes, getCurrentToken()); |
| } |
| |
| @Override |
| protected XmlAttributeNode createAttributeNode(Token name, Token equals, Token value) { |
| return new XmlAttributeNode(name, equals, value); |
| } |
| |
| @Override |
| protected XmlTagNode createTagNode(Token nodeStart, Token tag, List<XmlAttributeNode> attributes, |
| Token attributeEnd, List<XmlTagNode> tagNodes, Token contentEnd, Token closingTag, |
| Token nodeEnd) { |
| if (isScriptNode(tag, attributes, tagNodes)) { |
| HtmlScriptTagNode tagNode = new HtmlScriptTagNode( |
| nodeStart, |
| tag, |
| attributes, |
| attributeEnd, |
| tagNodes, |
| contentEnd, |
| closingTag, |
| nodeEnd); |
| String contents = tagNode.getContent(); |
| int contentOffset = attributeEnd.getEnd(); |
| Location location = lineInfo.getLocation(contentOffset); |
| Scanner scanner = new Scanner( |
| getSource(), |
| new SubSequenceReader(contents, contentOffset), |
| errorListener); |
| scanner.setSourceStart(location.getLineNumber(), location.getColumnNumber()); |
| com.google.dart.engine.scanner.Token firstToken = scanner.tokenize(); |
| Parser parser = new Parser(getSource(), errorListener); |
| CompilationUnit unit = parser.parseCompilationUnit(firstToken); |
| unit.setLineInfo(lineInfo); |
| tagNode.setScript(unit); |
| return tagNode; |
| } |
| return new XmlTagNode( |
| nodeStart, |
| tag, |
| attributes, |
| attributeEnd, |
| tagNodes, |
| contentEnd, |
| closingTag, |
| nodeEnd); |
| } |
| |
| @Override |
| protected boolean isSelfClosing(Token tag) { |
| return SELF_CLOSING.contains(tag.getLexeme()); |
| } |
| |
| /** |
| * Determine if the specified node is a Dart script. |
| * |
| * @param node the node to be tested (not {@code null}) |
| * @return {@code true} if the node is a Dart script |
| */ |
| private boolean isScriptNode(Token tag, List<XmlAttributeNode> attributes, |
| List<XmlTagNode> tagNodes) { |
| if (tagNodes.size() != 0 || !tag.getLexeme().equals(SCRIPT)) { |
| return false; |
| } |
| for (XmlAttributeNode attribute : attributes) { |
| if (attribute.getName().equals(TYPE)) { |
| Token valueToken = attribute.getValueToken(); |
| if (valueToken != null) { |
| String value = valueToken.getLexeme(); |
| if (value.equals(APPLICATION_DART_IN_DOUBLE_QUOTES) |
| || value.equals(APPLICATION_DART_IN_SINGLE_QUOTES)) { |
| return true; |
| } |
| } |
| } |
| } |
| return false; |
| } |
| } |