/* * Copyright (c) 2014, 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.internal.builder; import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.Lists; import com.google.dart.engine.ast.Annotation; import com.google.dart.engine.ast.ArgumentList; import com.google.dart.engine.ast.AssignmentExpression; import com.google.dart.engine.ast.AstNode; import com.google.dart.engine.ast.ClassDeclaration; import com.google.dart.engine.ast.ClassMember; import com.google.dart.engine.ast.CompilationUnit; import com.google.dart.engine.ast.CompilationUnitMember; import com.google.dart.engine.ast.Expression; import com.google.dart.engine.ast.FieldDeclaration; import com.google.dart.engine.ast.IndexExpression; import com.google.dart.engine.ast.MapLiteral; import com.google.dart.engine.ast.MapLiteralEntry; import com.google.dart.engine.ast.MethodInvocation; import com.google.dart.engine.ast.NamedExpression; import com.google.dart.engine.ast.NodeList; import com.google.dart.engine.ast.PrefixedIdentifier; import com.google.dart.engine.ast.SimpleIdentifier; import com.google.dart.engine.ast.SimpleStringLiteral; import com.google.dart.engine.ast.VariableDeclaration; import com.google.dart.engine.ast.visitor.RecursiveAstVisitor; import com.google.dart.engine.element.ClassElement; import com.google.dart.engine.element.ConstructorElement; import com.google.dart.engine.element.Element; import com.google.dart.engine.element.FieldElement; import com.google.dart.engine.element.LibraryElement; import com.google.dart.engine.element.PropertyAccessorElement; import com.google.dart.engine.element.ToolkitObjectElement; import com.google.dart.engine.element.VariableElement; import com.google.dart.engine.element.angular.AngularComponentElement; import com.google.dart.engine.element.angular.AngularDecoratorElement; import com.google.dart.engine.element.angular.AngularElement; import com.google.dart.engine.element.angular.AngularHasSelectorElement; import com.google.dart.engine.element.angular.AngularPropertyElement; import com.google.dart.engine.element.angular.AngularPropertyKind; import com.google.dart.engine.element.angular.AngularScopePropertyElement; import com.google.dart.engine.element.angular.AngularSelectorElement; import com.google.dart.engine.element.angular.AngularViewElement; import com.google.dart.engine.error.AnalysisError; import com.google.dart.engine.error.AnalysisErrorListener; import com.google.dart.engine.error.AngularCode; import com.google.dart.engine.error.ErrorCode; import com.google.dart.engine.internal.element.ClassElementImpl; import com.google.dart.engine.internal.element.CompilationUnitElementImpl; import com.google.dart.engine.internal.element.angular.AngularComponentElementImpl; import com.google.dart.engine.internal.element.angular.AngularControllerElementImpl; import com.google.dart.engine.internal.element.angular.AngularDecoratorElementImpl; import com.google.dart.engine.internal.element.angular.AngularFormatterElementImpl; import com.google.dart.engine.internal.element.angular.AngularHasClassSelectorElementImpl; import com.google.dart.engine.internal.element.angular.AngularPropertyElementImpl; import com.google.dart.engine.internal.element.angular.AngularScopePropertyElementImpl; import com.google.dart.engine.internal.element.angular.AngularTagSelectorElementImpl; import com.google.dart.engine.internal.element.angular.AngularViewElementImpl; import com.google.dart.engine.internal.element.angular.HasAttributeSelectorElementImpl; import com.google.dart.engine.internal.element.angular.IsTagHasAttributeSelectorElementImpl; import com.google.dart.engine.source.Source; import com.google.dart.engine.type.InterfaceType; import com.google.dart.engine.type.Type; import com.google.dart.engine.utilities.general.ObjectUtilities; import com.google.dart.engine.utilities.general.StringUtilities; import java.util.List; /** * Instances of the class {@code AngularCompilationUnitBuilder} build an Angular specific element * model for a single compilation unit. * * @coverage dart.engine.resolver */ public class AngularCompilationUnitBuilder { private static final String NG_COMPONENT = "Component"; private static final String NG_CONTROLLER = "Controller"; private static final String NG_DECORATOR = "Decorator"; private static final String NG_FORMATTER = "Formatter"; private static final String NAME = "name"; private static final String SELECTOR = "selector"; private static final String PUBLISH_AS = "publishAs"; private static final String TEMPLATE_URL = "templateUrl"; private static final String CSS_URL = "cssUrl"; private static final String NG_ATTR = "NgAttr"; private static final String NG_CALLBACK = "NgCallback"; private static final String NG_ONE_WAY = "NgOneWay"; private static final String NG_ONE_WAY_ONE_TIME = "NgOneWayOneTime"; private static final String NG_TWO_WAY = "NgTwoWay"; public static Element getElement(AstNode node, int offset) { // maybe node is not SimpleStringLiteral if (!(node instanceof SimpleStringLiteral)) { return null; } SimpleStringLiteral literal = (SimpleStringLiteral) node; // maybe has AngularElement { Element element = literal.getToolkitElement(); if (element instanceof AngularElement) { return element; } } // prepare enclosing ClassDeclaration ClassDeclaration classDeclaration = node.getAncestor(ClassDeclaration.class); if (classDeclaration == null) { return null; } // prepare ClassElement ClassElement classElement = classDeclaration.getElement(); if (classElement == null) { return null; } // check toolkit objects for (ToolkitObjectElement toolkitObject : classElement.getToolkitObjects()) { AngularPropertyElement[] properties = AngularPropertyElement.EMPTY_ARRAY; // maybe name if (toolkitObject instanceof AngularElement) { if (isNameCoveredByLiteral(toolkitObject, node)) { return toolkitObject; } } // try selector if (toolkitObject instanceof AngularHasSelectorElement) { AngularHasSelectorElement hasSelector = (AngularHasSelectorElement) toolkitObject; AngularSelectorElement selector = hasSelector.getSelector(); if (isNameCoveredByLiteral(selector, node)) { return selector; } } // try properties of AngularComponentElement if (toolkitObject instanceof AngularComponentElement) { AngularComponentElement component = (AngularComponentElement) toolkitObject; properties = component.getProperties(); } // try properties of AngularDirectiveElement if (toolkitObject instanceof AngularDecoratorElement) { AngularDecoratorElement directive = (AngularDecoratorElement) toolkitObject; properties = directive.getProperties(); } // check properties for (AngularPropertyElement property : properties) { // property name (use complete node range) if (isNameCoveredByLiteral(property, node)) { return property; } // field name (use complete node range, including @, => and <=>) FieldElement field = property.getField(); if (field != null) { int fieldOffset = property.getFieldNameOffset(); int fieldEnd = fieldOffset + field.getName().length(); if (node.getOffset() <= fieldOffset && fieldEnd < node.getEnd()) { return field; } } } } // no Element return null; } /** * Parses given selector text and returns {@link AngularSelectorElement}. May be {@code null} if * cannot parse. */ @VisibleForTesting public static AngularSelectorElement parseSelector(int offset, String text) { // [attribute] if (StringUtilities.startsWithChar(text, '[') && StringUtilities.endsWithChar(text, ']')) { int nameOffset = offset + 1; String attributeName = text.substring(1, text.length() - 1); // TODO(scheglov) report warning if there are spaces between [ and identifier return new HasAttributeSelectorElementImpl(attributeName, nameOffset); } // .class if (StringUtilities.startsWithChar(text, '.')) { int nameOffset = offset + 1; String className = text.substring(1, text.length()); return new AngularHasClassSelectorElementImpl(className, nameOffset); } // tag[attribute] if (StringUtilities.endsWithChar(text, ']')) { int index = StringUtilities.indexOf1(text, 0, '['); if (index != -1) { String tagName = text.substring(0, index); String attributeName = text.substring(index + 1, text.length() - 1); if (StringUtilities.isTagName(tagName)) { return new IsTagHasAttributeSelectorElementImpl(tagName, attributeName); } } } // tag if (StringUtilities.isTagName(text)) { return new AngularTagSelectorElementImpl(text, offset); } return null; } /** * Returns the {@link FieldElement} of the first field in the given {@link FieldDeclaration}. */ private static FieldElement getOnlyFieldElement(FieldDeclaration fieldDeclaration) { NodeList<VariableDeclaration> fields = fieldDeclaration.getFields().getVariables(); return (FieldElement) fields.get(0).getElement(); } /** * If given {@link Annotation} has one argument and it is {@link SimpleStringLiteral}, returns it, * otherwise returns {@code null}. */ private static SimpleStringLiteral getOnlySimpleStringLiteralArgument(Annotation annotation) { SimpleStringLiteral nameLiteral = null; ArgumentList argsNode = annotation.getArguments(); if (argsNode != null) { NodeList<Expression> args = argsNode.getArguments(); if (args.size() == 1) { Expression arg = args.get(0); if (arg instanceof SimpleStringLiteral) { nameLiteral = (SimpleStringLiteral) arg; } } } return nameLiteral; } /** * Checks if the name range of the given {@link Element} is completely covered by the given * {@link SimpleStringLiteral}. */ private static boolean isNameCoveredByLiteral(Element element, AstNode node) { if (element != null) { String name = element.getName(); if (name != null) { int nameOffset = element.getNameOffset(); int nameEnd = nameOffset + name.length(); return node.getOffset() <= nameOffset && nameEnd < node.getEnd(); } } return false; } /** * Parses given {@link SimpleStringLiteral} using {@link #parseSelector(int, String)}. */ private static AngularSelectorElement parseSelectorFromString(SimpleStringLiteral literal) { int offset = literal.getValueOffset(); String text = literal.getStringValue(); return parseSelector(offset, text); } /** * The listener to which errors will be reported. */ private final AnalysisErrorListener errorListener; /** * The source containing the unit that will be analyzed. */ private final Source source; /** * The compilation unit with built Dart element models. */ private CompilationUnit unit; /** * The {@link ClassDeclaration} that is currently being analyzed. */ private ClassDeclaration classDeclaration; /** * The {@link ClassElementImpl} that is currently being analyzed. */ private ClassElementImpl classElement; /** * The {@link Annotation} that is currently being analyzed. */ private Annotation annotation; /** * Initialize a newly created compilation unit element builder. * * @param errorListener the listener to which errors will be reported. * @param source the source containing the unit that will be analyzed * @param unit the compilation unit with built Dart element models */ public AngularCompilationUnitBuilder(AnalysisErrorListener errorListener, Source source, CompilationUnit unit) { this.errorListener = errorListener; this.source = source; this.unit = unit; } /** * Builds Angular specific element models and adds them to the existing Dart elements. */ public void build() { parseViews(); // process classes for (CompilationUnitMember unitMember : unit.getDeclarations()) { if (unitMember instanceof ClassDeclaration) { this.classDeclaration = (ClassDeclaration) unitMember; this.classElement = (ClassElementImpl) classDeclaration.getElement(); // process annotations NodeList<Annotation> annotations = classDeclaration.getMetadata(); for (Annotation annotation : annotations) { // verify annotation if (annotation.getArguments() == null) { continue; } this.annotation = annotation; // @Formatter if (isAngularAnnotation(annotation, NG_FORMATTER)) { parseFormatter(); continue; } // @Component if (isAngularAnnotation(annotation, NG_COMPONENT)) { parseComponent(); continue; } // @Controller if (isAngularAnnotation(annotation, NG_CONTROLLER)) { parseController(); continue; } // @Decorator if (isAngularAnnotation(annotation, NG_DECORATOR)) { parseDecorator(); continue; } } } } } /** * @return the argument {@link Expression} with given name form {@link #annotation}, may be * {@code null} if not found. */ private Expression getArgument(String name) { List<Expression> arguments = annotation.getArguments().getArguments(); for (Expression argument : arguments) { if (argument instanceof NamedExpression) { NamedExpression namedExpression = (NamedExpression) argument; String argumentName = namedExpression.getName().getLabel().getName(); if (name.equals(argumentName)) { return namedExpression.getExpression(); } } } return null; } /** * @return the {@link String} value of the named argument. */ private String getStringArgument(String name) { return getStringLiteral(name).getValue(); } /** * @return the offset of the value of the named argument. */ private int getStringArgumentOffset(String name) { Expression argument = getArgument(name); return ((SimpleStringLiteral) argument).getValueOffset(); } /** * @return the {@link SimpleStringLiteral} of the named argument. */ private SimpleStringLiteral getStringLiteral(String name) { Expression argument = getArgument(name); return (SimpleStringLiteral) argument; } /** * Checks if {@link #namedArguments} has string value for the argument with the given name. */ private boolean hasStringArgument(String name) { Expression argument = getArgument(name); return argument instanceof SimpleStringLiteral; } /** * Checks if given {@link Annotation} is an annotation with required name. */ private boolean isAngularAnnotation(Annotation annotation, String name) { Element element = annotation.getElement(); if (element instanceof ConstructorElement) { ConstructorElement constructorElement = (ConstructorElement) element; if (!constructorElement.getReturnType().getDisplayName().equals(name)) { return false; } return isAngularLibraryElement(constructorElement); } return false; } /** * Checks if the given {@link Element} is a part of the Angular library. */ private boolean isAngularLibraryElement(Element element) { LibraryElement library = element.getLibrary(); return library != null && library.getName() != null && library.getName().startsWith("angular"); } private void parseComponent() { boolean isValid = true; // publishAs String name = null; int nameOffset = -1; if (hasStringArgument(PUBLISH_AS)) { name = getStringArgument(PUBLISH_AS); nameOffset = getStringArgumentOffset(PUBLISH_AS); } // selector AngularSelectorElement selector = null; if (!hasStringArgument(SELECTOR)) { reportErrorForAnnotation(AngularCode.MISSING_SELECTOR); isValid = false; } else { SimpleStringLiteral selectorLiteral = getStringLiteral(SELECTOR); selector = parseSelectorFromString(selectorLiteral); if (selector == null) { reportErrorForArgument(SELECTOR, AngularCode.CANNOT_PARSE_SELECTOR, selectorLiteral); isValid = false; } } // templateUrl String templateUri = null; int templateUriOffset = -1; if (hasStringArgument(TEMPLATE_URL)) { templateUri = getStringArgument(TEMPLATE_URL); templateUriOffset = getStringArgumentOffset(TEMPLATE_URL); } // cssUrl String styleUri = null; int styleUriOffset = -1; if (hasStringArgument(CSS_URL)) { styleUri = getStringArgument(CSS_URL); styleUriOffset = getStringArgumentOffset(CSS_URL); } // create if (isValid) { AngularComponentElementImpl element = new AngularComponentElementImpl( name, nameOffset, annotation.getOffset()); element.setSelector(selector); element.setTemplateUri(templateUri); element.setTemplateUriOffset(templateUriOffset); element.setStyleUri(styleUri); element.setStyleUriOffset(styleUriOffset); element.setProperties(parseComponentProperties()); element.setScopeProperties(parseScopeProperties()); classElement.addToolkitObjects(element); } } /** * Parses {@link AngularPropertyElement}s from {@link #annotation} and {@link #classDeclaration}. */ private AngularPropertyElement[] parseComponentProperties() { List<AngularPropertyElement> properties = Lists.newArrayList(); parseComponentProperties_fromMap(properties); parseComponentProperties_fromFields(properties); return properties.toArray(new AngularPropertyElement[properties.size()]); } /** * Parses {@link AngularPropertyElement}s from {@link #annotation}. */ private void parseComponentProperties_fromFields(List<AngularPropertyElement> properties) { NodeList<ClassMember> members = classDeclaration.getMembers(); for (ClassMember member : members) { if (member instanceof FieldDeclaration) { FieldDeclaration fieldDeclaration = (FieldDeclaration) member; for (Annotation annotation : fieldDeclaration.getMetadata()) { // prepare property kind (if property annotation at all) AngularPropertyKind kind = null; if (isAngularAnnotation(annotation, NG_ATTR)) { kind = AngularPropertyKind.ATTR; } else if (isAngularAnnotation(annotation, NG_CALLBACK)) { kind = AngularPropertyKind.CALLBACK; } else if (isAngularAnnotation(annotation, NG_ONE_WAY)) { kind = AngularPropertyKind.ONE_WAY; } else if (isAngularAnnotation(annotation, NG_ONE_WAY_ONE_TIME)) { kind = AngularPropertyKind.ONE_WAY_ONE_TIME; } else if (isAngularAnnotation(annotation, NG_TWO_WAY)) { kind = AngularPropertyKind.TWO_WAY; } // add property if (kind != null) { SimpleStringLiteral nameLiteral = getOnlySimpleStringLiteralArgument(annotation); FieldElement field = getOnlyFieldElement(fieldDeclaration); if (nameLiteral != null && field != null) { AngularPropertyElementImpl property = new AngularPropertyElementImpl( nameLiteral.getValue(), nameLiteral.getValueOffset()); property.setField(field); property.setPropertyKind(kind); properties.add(property); } } } } } } /** * Parses {@link AngularPropertyElement}s from {@link #annotation}. */ private void parseComponentProperties_fromMap(List<AngularPropertyElement> properties) { Expression mapExpression = getArgument("map"); // may be not properties if (mapExpression == null) { return; } // prepare map literal if (!(mapExpression instanceof MapLiteral)) { reportErrorForNode(AngularCode.INVALID_PROPERTY_MAP, mapExpression); return; } MapLiteral mapLiteral = (MapLiteral) mapExpression; // analyze map entries for (MapLiteralEntry entry : mapLiteral.getEntries()) { // prepare property name Expression nameExpression = entry.getKey(); if (!(nameExpression instanceof SimpleStringLiteral)) { reportErrorForNode(AngularCode.INVALID_PROPERTY_NAME, nameExpression); continue; } SimpleStringLiteral nameLiteral = (SimpleStringLiteral) nameExpression; String name = nameLiteral.getValue(); int nameOffset = nameLiteral.getValueOffset(); // prepare field specification Expression specExpression = entry.getValue(); if (!(specExpression instanceof SimpleStringLiteral)) { reportErrorForNode(AngularCode.INVALID_PROPERTY_SPEC, specExpression); continue; } SimpleStringLiteral specLiteral = (SimpleStringLiteral) specExpression; String spec = specLiteral.getValue(); // parse binding kind and field name AngularPropertyKind kind; int fieldNameOffset; if (StringUtilities.startsWithChar(spec, '@')) { kind = AngularPropertyKind.ATTR; fieldNameOffset = 1; } else if (StringUtilities.startsWithChar(spec, '&')) { kind = AngularPropertyKind.CALLBACK; fieldNameOffset = 1; } else if (StringUtilities.startsWith3(spec, 0, '=', '>', '!')) { kind = AngularPropertyKind.ONE_WAY_ONE_TIME; fieldNameOffset = 3; } else if (StringUtilities.startsWith2(spec, 0, '=', '>')) { kind = AngularPropertyKind.ONE_WAY; fieldNameOffset = 2; } else if (StringUtilities.startsWith3(spec, 0, '<', '=', '>')) { kind = AngularPropertyKind.TWO_WAY; fieldNameOffset = 3; } else { reportErrorForNode(AngularCode.INVALID_PROPERTY_KIND, specLiteral, spec); continue; } String fieldName = spec.substring(fieldNameOffset); fieldNameOffset += specLiteral.getValueOffset(); // prepare field PropertyAccessorElement setter = classElement.getType().lookUpSetter( fieldName, classElement.getLibrary()); if (setter == null) { reportErrorForOffset( AngularCode.INVALID_PROPERTY_FIELD, fieldNameOffset, fieldName.length(), fieldName); continue; } FieldElement field = (FieldElement) setter.getVariable(); // add property AngularPropertyElementImpl property = new AngularPropertyElementImpl(name, nameOffset); property.setField(field); property.setPropertyKind(kind); property.setFieldNameOffset(fieldNameOffset); properties.add(property); } } private void parseController() { boolean isValid = true; // publishAs if (!hasStringArgument(PUBLISH_AS)) { reportErrorForAnnotation(AngularCode.MISSING_PUBLISH_AS); isValid = false; } // selector AngularSelectorElement selector = null; if (!hasStringArgument(SELECTOR)) { reportErrorForAnnotation(AngularCode.MISSING_SELECTOR); isValid = false; } else { SimpleStringLiteral selectorLiteral = getStringLiteral(SELECTOR); selector = parseSelectorFromString(selectorLiteral); if (selector == null) { reportErrorForArgument(SELECTOR, AngularCode.CANNOT_PARSE_SELECTOR, selectorLiteral); isValid = false; } } // create if (isValid) { String name = getStringArgument(PUBLISH_AS); int nameOffset = getStringArgumentOffset(PUBLISH_AS); AngularControllerElementImpl element = new AngularControllerElementImpl(name, nameOffset); element.setSelector(selector); classElement.addToolkitObjects(element); } } private void parseDecorator() { boolean isValid = true; // selector AngularSelectorElement selector = null; if (!hasStringArgument(SELECTOR)) { reportErrorForAnnotation(AngularCode.MISSING_SELECTOR); isValid = false; } else { SimpleStringLiteral selectorLiteral = getStringLiteral(SELECTOR); selector = parseSelectorFromString(selectorLiteral); if (selector == null) { reportErrorForArgument(SELECTOR, AngularCode.CANNOT_PARSE_SELECTOR, selectorLiteral); isValid = false; } } // create if (isValid) { int offset = annotation.getOffset(); AngularDecoratorElementImpl element = new AngularDecoratorElementImpl(offset); element.setSelector(selector); element.setProperties(parseComponentProperties()); classElement.addToolkitObjects(element); } } private void parseFormatter() { boolean isValid = true; // name if (!hasStringArgument(NAME)) { reportErrorForAnnotation(AngularCode.MISSING_NAME); isValid = false; } // create if (isValid) { String name = getStringArgument(NAME); int nameOffset = getStringArgumentOffset(NAME); classElement.addToolkitObjects(new AngularFormatterElementImpl(name, nameOffset)); } } private AngularScopePropertyElement[] parseScopeProperties() { final List<AngularScopePropertyElement> properties = Lists.newArrayList(); classDeclaration.accept(new RecursiveAstVisitor<Void>() { @Override public Void visitAssignmentExpression(AssignmentExpression node) { addProperty(node); return super.visitAssignmentExpression(node); } private void addProperty(AssignmentExpression node) { // try to find "name" in scope[name] SimpleStringLiteral nameNode = getNameNode(node.getLeftHandSide()); if (nameNode == null) { return; } // prepare unique String name = nameNode.getStringValue(); if (hasPropertyWithName(name)) { return; } // do add property int nameOffset = nameNode.getValueOffset(); AngularScopePropertyElement property = new AngularScopePropertyElementImpl( name, nameOffset, node.getRightHandSide().getBestType()); nameNode.setToolkitElement(property); properties.add(property); } private SimpleStringLiteral getNameNode(Expression node) { if (node instanceof IndexExpression) { IndexExpression indexExpression = (IndexExpression) node; Expression target = indexExpression.getTarget(); Expression index = indexExpression.getIndex(); if (index instanceof SimpleStringLiteral && isContext(target)) { return (SimpleStringLiteral) index; } } return null; } private boolean hasPropertyWithName(String name) { for (AngularScopePropertyElement property : properties) { if (property.getName().equals(name)) { return true; } } return false; } private boolean isContext(Expression target) { if (target instanceof PrefixedIdentifier) { PrefixedIdentifier prefixed = (PrefixedIdentifier) target; SimpleIdentifier prefix = prefixed.getPrefix(); SimpleIdentifier identifier = prefixed.getIdentifier(); return ObjectUtilities.equals(identifier.getName(), "context") && isScope(prefix); } return false; } private boolean isScope(Expression target) { if (target != null) { Type type = target.getBestType(); if (type instanceof InterfaceType) { InterfaceType interfaceType = (InterfaceType) type; return interfaceType.getName().equals("Scope"); } } return false; } }); return properties.toArray(new AngularScopePropertyElement[properties.size()]); } /** * Create {@link AngularViewElement} for each valid <code>view('template.html')</code> invocation, * where <code>view</code> is <code>ViewFactory</code>. */ private void parseViews() { final List<AngularViewElement> views = Lists.newArrayList(); unit.accept(new RecursiveAstVisitor<Void>() { @Override public Void visitMethodInvocation(MethodInvocation node) { addView(node); return super.visitMethodInvocation(node); } private void addView(MethodInvocation node) { // only one argument List<Expression> arguments = node.getArgumentList().getArguments(); if (arguments.size() != 1) { return; } // String literal Expression argument = arguments.get(0); if (!(argument instanceof SimpleStringLiteral)) { return; } SimpleStringLiteral literal = (SimpleStringLiteral) argument; // just view('template') if (node.getRealTarget() != null) { return; } // should be ViewFactory if (!isViewFactory(node.getMethodName())) { return; } // add AngularViewElement String templateUri = literal.getStringValue(); int templateUriOffset = literal.getValueOffset(); views.add(new AngularViewElementImpl(templateUri, templateUriOffset)); } private boolean isViewFactory(Expression target) { if (target instanceof SimpleIdentifier) { SimpleIdentifier identifier = (SimpleIdentifier) target; Element element = identifier.getStaticElement(); if (element instanceof VariableElement) { VariableElement variable = (VariableElement) element; Type type = variable.getType(); if (type instanceof InterfaceType) { InterfaceType interfaceType = (InterfaceType) type; return interfaceType.getName().equals("ViewFactory"); } } } return false; } }); if (!views.isEmpty()) { AngularViewElement[] viewArray = views.toArray(new AngularViewElement[views.size()]); ((CompilationUnitElementImpl) unit.getElement()).setAngularViews(viewArray); } } private void reportErrorForAnnotation(ErrorCode errorCode, Object... arguments) { reportErrorForNode(errorCode, annotation, arguments); } private void reportErrorForArgument(String argumentName, ErrorCode errorCode, Object... arguments) { Expression argument = getArgument(argumentName); reportErrorForNode(errorCode, argument, arguments); } private void reportErrorForNode(ErrorCode errorCode, AstNode node, Object... arguments) { int offset = node.getOffset(); int length = node.getLength(); reportErrorForOffset(errorCode, offset, length, arguments); } private void reportErrorForOffset(ErrorCode errorCode, int offset, int length, Object... arguments) { errorListener.onError(new AnalysisError(source, offset, length, errorCode, arguments)); } }