| // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| package com.google.dart.compiler.type; |
| |
| import com.google.common.collect.Maps; |
| import com.google.dart.compiler.ast.DartExpression; |
| import com.google.dart.compiler.ast.DartMethodInvocation; |
| import com.google.dart.compiler.ast.DartStringLiteral; |
| import com.google.dart.compiler.ast.DartUnqualifiedInvocation; |
| import com.google.dart.compiler.resolver.Element; |
| import com.google.dart.compiler.resolver.ElementKind; |
| import com.google.dart.compiler.resolver.Elements; |
| import com.google.dart.compiler.resolver.EnclosingElement; |
| import com.google.dart.compiler.resolver.LibraryElement; |
| import com.google.dart.compiler.util.apache.StringUtils; |
| |
| import java.util.List; |
| import java.util.Map; |
| |
| /** |
| * Provides type information which cannot be inferred from source itself. |
| */ |
| public class ExternalTypeAnalyzers { |
| private static final Map<String, String> tagToElementType = Maps.newHashMap(); |
| static { |
| tagToElementType.put("A", "AnchorElement"); |
| tagToElementType.put("AREA", "AreaElement"); |
| tagToElementType.put("BR", "BRElement"); |
| tagToElementType.put("BASE", "BaseElement"); |
| tagToElementType.put("BODY", "BodyElement"); |
| tagToElementType.put("BUTTON", "ButtonElement"); |
| tagToElementType.put("CANVAS", "CanvasElement"); |
| tagToElementType.put("DL", "DListElement"); |
| tagToElementType.put("DETAILS", "DetailsElement"); |
| tagToElementType.put("DIV", "DivElement"); |
| tagToElementType.put("EMBED", "EmbedElement"); |
| tagToElementType.put("FIELDSET", "FieldSetElement"); |
| tagToElementType.put("FORM", "FormElement"); |
| tagToElementType.put("HR", "HRElement"); |
| tagToElementType.put("HEAD", "HeadElement"); |
| tagToElementType.put("H1", "HeadingElement"); |
| tagToElementType.put("H2", "HeadingElement"); |
| tagToElementType.put("H3", "HeadingElement"); |
| tagToElementType.put("H4", "HeadingElement"); |
| tagToElementType.put("H5", "HeadingElement"); |
| tagToElementType.put("H6", "HeadingElement"); |
| tagToElementType.put("HTML", "HtmlElement"); |
| tagToElementType.put("IFRAME", "IFrameElement"); |
| tagToElementType.put("IMG", "ImageElement"); |
| tagToElementType.put("INPUT", "InputElement"); |
| tagToElementType.put("KEYGEN", "KeygenElement"); |
| tagToElementType.put("LI", "LIElement"); |
| tagToElementType.put("LABEL", "LabelElement"); |
| tagToElementType.put("LEGEND", "LegendElement"); |
| tagToElementType.put("LINK", "LinkElement"); |
| tagToElementType.put("MAP", "MapElement"); |
| tagToElementType.put("MENU", "MenuElement"); |
| tagToElementType.put("METER", "MeterElement"); |
| tagToElementType.put("OL", "OListElement"); |
| tagToElementType.put("OBJECT", "ObjectElement"); |
| tagToElementType.put("OPTGROUP", "OptGroupElement"); |
| tagToElementType.put("OUTPUT", "OutputElement"); |
| tagToElementType.put("P", "ParagraphElement"); |
| tagToElementType.put("PARAM", "ParamElement"); |
| tagToElementType.put("PRE", "PreElement"); |
| tagToElementType.put("PROGRESS", "ProgressElement"); |
| tagToElementType.put("SCRIPT", "ScriptElement"); |
| tagToElementType.put("SELECT", "SelectElement"); |
| tagToElementType.put("SOURCE", "SourceElement"); |
| tagToElementType.put("SPAN", "SpanElement"); |
| tagToElementType.put("STYLE", "StyleElement"); |
| tagToElementType.put("CAPTION", "TableCaptionElement"); |
| tagToElementType.put("TD", "TableCellElement"); |
| tagToElementType.put("COL", "TableColElement"); |
| tagToElementType.put("TABLE", "TableElement"); |
| tagToElementType.put("TR", "TableRowElement"); |
| tagToElementType.put("TEXTAREA", "TextAreaElement"); |
| tagToElementType.put("TITLE", "TitleElement"); |
| tagToElementType.put("TRACK", "TrackElement"); |
| tagToElementType.put("UL", "UListElement"); |
| tagToElementType.put("VIDEO", "VideoElement"); |
| tagToElementType.put("DART_EDITOR_NO_SUCH_TYPE", "DartEditorNoSuchElement"); |
| } |
| |
| /** |
| * Attempts to make better guess about return type of invocation. |
| * |
| * @return the better {@link Type} guess, may be "defaultType" if cannot make better guess. |
| */ |
| public static Type resolve(Types types, DartUnqualifiedInvocation invocation, Element element, |
| Type defaultType) { |
| if (element == null) { |
| return defaultType; |
| } |
| String name = element.getName(); |
| List<DartExpression> arguments = invocation.getArguments(); |
| LibraryElement libraryElement = Elements.getDeclaringLibrary(element); |
| // html.query(String) |
| if ("query".equals(name) && isHtmlLibraryFunction(element)) { |
| return analyzeQuery(arguments, libraryElement, defaultType); |
| } |
| // no guess |
| return defaultType; |
| } |
| |
| /** |
| * Attempts to make better guess about return type of invocation. |
| * |
| * @return the better {@link Type} guess, may be "defaultType" if cannot make better guess. |
| */ |
| public static Type resolve(Types types, DartMethodInvocation invocation, Element element, |
| Type defaultType) { |
| if (element == null) { |
| return defaultType; |
| } |
| String name = element.getName(); |
| List<DartExpression> arguments = invocation.getArguments(); |
| LibraryElement libraryElement = Elements.getDeclaringLibrary(element); |
| // NodeSelector.query(String) |
| EnclosingElement enclosingElement = element.getEnclosingElement(); |
| if ("query".equals(name) && isDeclaredInHtmlLibrary(element) |
| && ElementKind.of(enclosingElement) == ElementKind.CLASS |
| && "NodeSelector".equals(enclosingElement.getName())) { |
| return analyzeQuery(arguments, libraryElement, defaultType); |
| } |
| // no guess |
| return defaultType; |
| } |
| |
| private static Type analyzeQuery(List<DartExpression> arguments, LibraryElement libraryElement, |
| Type defaultType) { |
| if (arguments.size() == 1 && arguments.get(0) instanceof DartStringLiteral) { |
| String selectors = ((DartStringLiteral) arguments.get(0)).getValue(); |
| // if has spaces, full parsing required, because may be: E[text='warning text'] |
| if (selectors.contains(" ")) { |
| return defaultType; |
| } |
| // try to extract tag |
| // http://www.w3.org/TR/CSS2/selector.html |
| String tag = selectors; |
| tag = StringUtils.substringBefore(tag, ":"); |
| tag = StringUtils.substringBefore(tag, "["); |
| tag = StringUtils.substringBefore(tag, "."); |
| tag = StringUtils.substringBefore(tag, "#"); |
| tag = tag.toUpperCase(); |
| // prepare Element type name |
| String tagTypeName = tagToElementType.get(tag); |
| if (tagTypeName == null) { |
| return defaultType; |
| } |
| // lookup tag Element |
| Element tagTypeElement = libraryElement.lookupLocalElement(tagTypeName); |
| if (tagTypeElement == null) { |
| return defaultType; |
| } |
| // OK, we know more specific return type |
| Type tagType = tagTypeElement.getType(); |
| if (tagType != null) { |
| return tagType; |
| } |
| } |
| // no guess |
| return defaultType; |
| } |
| |
| private static boolean isHtmlLibraryFunction(Element element) { |
| return ElementKind.of(element) == ElementKind.METHOD && isDeclaredInHtmlLibrary(element); |
| } |
| |
| private static boolean isDeclaredInHtmlLibrary(Element element) { |
| LibraryElement libraryElement = Elements.getDeclaringLibrary(element); |
| return StringUtils.startsWith(libraryElement.getName(), "dart://html"); |
| } |
| } |