| Index: editor/tools/plugins/com.google.dart.engine/src/com/google/dart/engine/html/parser/HtmlParser.java
|
| diff --git a/editor/tools/plugins/com.google.dart.engine/src/com/google/dart/engine/html/parser/HtmlParser.java b/editor/tools/plugins/com.google.dart.engine/src/com/google/dart/engine/html/parser/HtmlParser.java
|
| index 7377e7f365b2c5ae954de682e04281efc7342ede..4d1e01842cdced93c6676a594cf95b8c1bb43fcf 100644
|
| --- a/editor/tools/plugins/com.google.dart.engine/src/com/google/dart/engine/html/parser/HtmlParser.java
|
| +++ b/editor/tools/plugins/com.google.dart.engine/src/com/google/dart/engine/html/parser/HtmlParser.java
|
| @@ -13,14 +13,29 @@
|
| */
|
| 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.AttributeWithEmbeddedExpressions;
|
| +import com.google.dart.engine.html.ast.EmbeddedExpression;
|
| +import com.google.dart.engine.html.ast.HtmlScriptTagNode;
|
| import com.google.dart.engine.html.ast.HtmlUnit;
|
| +import com.google.dart.engine.html.ast.TagWithEmbeddedExpressions;
|
| +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.HtmlScanResult;
|
| import com.google.dart.engine.html.scanner.HtmlScanner;
|
| import com.google.dart.engine.html.scanner.Token;
|
| +import com.google.dart.engine.html.scanner.TokenType;
|
| +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.ArrayList;
|
| import java.util.Arrays;
|
| import java.util.HashSet;
|
| import java.util.List;
|
| @@ -33,7 +48,26 @@ import java.util.Set;
|
| * @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 OPENING_DELIMITER = "{{";
|
| + private static final String CLOSING_DELIMITER = "}}";
|
| + 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", "!", "h1", "h2", "h3", "h4", "h5", "h6"}));
|
| @@ -42,9 +76,34 @@ public class HtmlParser extends XmlParser {
|
| * 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) {
|
| + public HtmlParser(Source source, AnalysisErrorListener errorListener) {
|
| super(source);
|
| + this.errorListener = errorListener;
|
| + }
|
| +
|
| + public Token getEndToken(Token tag, List<XmlAttributeNode> attributes, Token attributeEnd,
|
| + List<XmlTagNode> tagNodes, Token contentEnd, Token closingTag, Token nodeEnd) {
|
| + if (nodeEnd != null) {
|
| + return nodeEnd;
|
| + }
|
| + if (closingTag != null) {
|
| + return closingTag;
|
| + }
|
| + if (contentEnd != null) {
|
| + return contentEnd;
|
| + }
|
| + if (!tagNodes.isEmpty()) {
|
| + return tagNodes.get(tagNodes.size() - 1).getEndToken();
|
| + }
|
| + if (attributeEnd != null) {
|
| + return attributeEnd;
|
| + }
|
| + if (!attributes.isEmpty()) {
|
| + return attributes.get(attributes.size() - 1).getEndToken();
|
| + }
|
| + return tag;
|
| }
|
|
|
| /**
|
| @@ -54,6 +113,8 @@ public class HtmlParser extends XmlParser {
|
| * @return the parse result (not {@code null})
|
| */
|
| public HtmlParseResult parse(HtmlScanResult scanResult) {
|
| + int[] lineStarts = scanResult.getLineStarts();
|
| + lineInfo = new LineInfo(lineStarts);
|
| Token firstToken = scanResult.getToken();
|
| List<XmlTagNode> tagNodes = parseTopTagNodes(firstToken);
|
| HtmlUnit unit = new HtmlUnit(firstToken, tagNodes, getCurrentToken());
|
| @@ -77,7 +138,159 @@ public class HtmlParser extends XmlParser {
|
| }
|
|
|
| @Override
|
| + protected XmlAttributeNode createAttributeNode(Token name, Token equals, Token value) {
|
| + ArrayList<EmbeddedExpression> expressions = new ArrayList<EmbeddedExpression>();
|
| + addEmbeddedExpressions(expressions, value);
|
| + if (expressions.isEmpty()) {
|
| + return new XmlAttributeNode(name, equals, value);
|
| + }
|
| + return new AttributeWithEmbeddedExpressions(
|
| + name,
|
| + equals,
|
| + value,
|
| + expressions.toArray(new EmbeddedExpression[expressions.size()]));
|
| + }
|
| +
|
| + @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;
|
| + }
|
| + Token token = nodeStart;
|
| + Token endToken = getEndToken(
|
| + tag,
|
| + attributes,
|
| + attributeEnd,
|
| + tagNodes,
|
| + contentEnd,
|
| + closingTag,
|
| + nodeEnd);
|
| + ArrayList<EmbeddedExpression> expressions = new ArrayList<EmbeddedExpression>();
|
| + while (token != endToken) {
|
| + if (token.getType() == TokenType.TEXT) {
|
| + addEmbeddedExpressions(expressions, token);
|
| + }
|
| + token = token.getNext();
|
| + }
|
| + if (expressions.isEmpty()) {
|
| + return super.createTagNode(
|
| + nodeStart,
|
| + tag,
|
| + attributes,
|
| + attributeEnd,
|
| + tagNodes,
|
| + contentEnd,
|
| + closingTag,
|
| + nodeEnd);
|
| + }
|
| + return new TagWithEmbeddedExpressions(
|
| + nodeStart,
|
| + tag,
|
| + attributes,
|
| + attributeEnd,
|
| + tagNodes,
|
| + contentEnd,
|
| + closingTag,
|
| + nodeEnd,
|
| + expressions.toArray(new EmbeddedExpression[expressions.size()]));
|
| + }
|
| +
|
| + @Override
|
| protected boolean isSelfClosing(Token tag) {
|
| return SELF_CLOSING.contains(tag.getLexeme());
|
| }
|
| +
|
| + /**
|
| + * Parse the value of the given token for embedded expressions, and add any embedded expressions
|
| + * that are found to the given list of expressions.
|
| + *
|
| + * @param expressions the list to which embedded expressions are to be added
|
| + * @param token the token whose value is to be parsed
|
| + */
|
| + private void addEmbeddedExpressions(ArrayList<EmbeddedExpression> expressions, Token token) {
|
| + String lexeme = token.getLexeme();
|
| + int startIndex = lexeme.indexOf(OPENING_DELIMITER);
|
| + while (startIndex >= 0) {
|
| + int endIndex = lexeme.indexOf(CLOSING_DELIMITER, startIndex + 2);
|
| + if (endIndex < 0) {
|
| + // TODO(brianwilkerson) Should we report this error or will it be reported by something else?
|
| + return;
|
| + }
|
| + int offset = token.getOffset();
|
| + expressions.add(new EmbeddedExpression(startIndex, parseEmbeddedExpression(
|
| + lexeme.substring(startIndex + 2, endIndex),
|
| + offset + startIndex), endIndex));
|
| + startIndex = lexeme.indexOf(OPENING_DELIMITER, endIndex + 2);
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * 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().getLexeme().equals(TYPE)) {
|
| + Token valueToken = attribute.getValue();
|
| + 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;
|
| + }
|
| +
|
| + /**
|
| + * 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 contents the contents of the expression
|
| + * @param contentOffset the offset of the expression in the larger file
|
| + * @return the Dart expression that was parsed
|
| + */
|
| + private Expression parseEmbeddedExpression(String contents, int contentOffset) {
|
| + 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);
|
| + return parser.parseExpression(firstToken);
|
| + }
|
| }
|
|
|