/*
* 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.ast;
import com.google.dart.engine.ast.AstNode;
import com.google.dart.engine.ast.Expression;
import com.google.dart.engine.ast.visitor.ElementLocator;
import com.google.dart.engine.ast.visitor.NodeLocator;
import com.google.dart.engine.element.Element;
import com.google.dart.engine.element.angular.AngularElement;
import com.google.dart.engine.html.ast.visitor.RecursiveXmlVisitor;
import com.google.dart.engine.html.scanner.Token;
import com.google.dart.engine.internal.html.angular.AngularHtmlUnitResolver;
import com.google.dart.engine.internal.html.angular.ExpressionVisitor;
/**
* Utilities locating {@link Expression}s and {@link Element}s in {@link HtmlUnit}.
*/
public class HtmlUnitUtils {
private static class FoundAttributeNodeError extends Error {
}
private static class FoundExpressionError extends Error {
}
private static class FoundTagNodeError extends Error {
}
/**
* Returns the {@link XmlAttributeNode} that is part of the given {@link HtmlUnit} and encloses
* the given offset.
*/
public static XmlAttributeNode getAttributeNode(HtmlUnit htmlUnit, final int offset) {
if (htmlUnit == null) {
return null;
}
final XmlAttributeNode[] result = {null};
try {
htmlUnit.accept(new RecursiveXmlVisitor<Void>() {
@Override
public Void visitXmlAttributeNode(XmlAttributeNode node) {
Token nameToken = node.getNameToken();
if (nameToken.getOffset() <= offset && offset <= nameToken.getEnd()) {
result[0] = node;
throw new FoundAttributeNodeError();
}
return super.visitXmlAttributeNode(node);
}
});
} catch (FoundAttributeNodeError e) {
return result[0];
}
return null;
}
/**
* Returns the best {@link Element} of the given {@link Expression}.
*/
public static Element getElement(Expression expression) {
if (expression == null) {
return null;
}
return ElementLocator.locate(expression);
}
/**
* Returns the {@link Element} of the {@link Expression} in the given {@link HtmlUnit}, enclosing
* the given offset.
*/
public static Element getElementAtOffset(HtmlUnit htmlUnit, int offset) {
Expression expression = getExpression(htmlUnit, offset);
return getElement(expression);
}
/**
* Returns the {@link Element} to open when requested at the given {@link Expression}.
*/
public static Element getElementToOpen(HtmlUnit htmlUnit, Expression expression) {
Element element = getElement(expression);
{
AngularElement angularElement = AngularHtmlUnitResolver.getAngularElement(element);
if (angularElement != null) {
return angularElement;
}
}
return element;
}
/**
* Returns the {@link XmlTagNode} that is part of the given {@link HtmlUnit} and encloses the
* given offset.
*/
public static XmlTagNode getEnclosingTagNode(HtmlUnit htmlUnit, final int offset) {
if (htmlUnit == null) {
return null;
}
final XmlTagNode[] result = {null};
try {
htmlUnit.accept(new RecursiveXmlVisitor<Void>() {
@Override
public Void visitXmlTagNode(XmlTagNode node) {
if (node.getOffset() <= offset && offset < node.getEnd()) {
result[0] = node;
super.visitXmlTagNode(node);
throw new FoundTagNodeError();
}
return null;
}
});
} catch (FoundTagNodeError e) {
return result[0];
}
return null;
}
/**
* Returns the {@link Expression} that is part of the given {@link HtmlUnit} and encloses the
* given offset.
*/
public static Expression getExpression(HtmlUnit htmlUnit, final int offset) {
if (htmlUnit == null) {
return null;
}
final Expression[] result = {null};
try {
// TODO(scheglov) this code is very Angular specific
htmlUnit.accept(new ExpressionVisitor() {
@Override
public void visitExpression(Expression expression) {
Expression at = getExpressionAt(expression, offset);
if (at != null) {
result[0] = at;
throw new FoundExpressionError();
}
}
});
} catch (FoundExpressionError e) {
return result[0];
}
return null;
}
/**
* Returns the {@link XmlTagNode} that is part of the given {@link HtmlUnit} and its open or
* closing tag name encloses the given offset.
*/
public static XmlTagNode getTagNode(HtmlUnit htmlUnit, int offset) {
XmlTagNode node = getEnclosingTagNode(htmlUnit, offset);
// do we have an enclosing tag at all?
if (node == null) {
return null;
}
// is "offset" in the open tag?
Token openTag = node.getTagToken();
if (openTag.getOffset() <= offset && offset <= openTag.getEnd()) {
return node;
}
// is "offset" in the open tag?
Token closeTag = node.getClosingTag();
if (closeTag != null && closeTag.getOffset() <= offset && offset <= closeTag.getEnd()) {
return node;
}
// not on a tag name
return null;
}
/**
* Returns the {@link Expression} that is part of the given root {@link AstNode} and encloses the
* given offset.
*/
private static Expression getExpressionAt(AstNode root, int offset) {
if (root.getOffset() <= offset && offset <= root.getEnd()) {
AstNode dartNode = new NodeLocator(offset).searchWithin(root);
if (dartNode instanceof Expression) {
return (Expression) dartNode;
}
}
return null;
}
}