Package com.google.dart.engine.internal.html.angular

Source Code of com.google.dart.engine.internal.html.angular.AngularHtmlUnitResolver$FoundAppError

/*
* 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.internal.html.angular;

import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.google.dart.engine.AnalysisEngine;
import com.google.dart.engine.ast.AstNode;
import com.google.dart.engine.ast.CompilationUnit;
import com.google.dart.engine.ast.Expression;
import com.google.dart.engine.ast.SimpleIdentifier;
import com.google.dart.engine.context.AnalysisContext;
import com.google.dart.engine.context.AnalysisException;
import com.google.dart.engine.element.ClassElement;
import com.google.dart.engine.element.CompilationUnitElement;
import com.google.dart.engine.element.Element;
import com.google.dart.engine.element.ExternalHtmlScriptElement;
import com.google.dart.engine.element.FunctionElement;
import com.google.dart.engine.element.HtmlScriptElement;
import com.google.dart.engine.element.ImportElement;
import com.google.dart.engine.element.LibraryElement;
import com.google.dart.engine.element.LocalVariableElement;
import com.google.dart.engine.element.ToolkitObjectElement;
import com.google.dart.engine.element.angular.AngularComponentElement;
import com.google.dart.engine.element.angular.AngularControllerElement;
import com.google.dart.engine.element.angular.AngularDecoratorElement;
import com.google.dart.engine.element.angular.AngularElement;
import com.google.dart.engine.element.angular.AngularFormatterElement;
import com.google.dart.engine.element.angular.AngularHasTemplateElement;
import com.google.dart.engine.element.angular.AngularScopePropertyElement;
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.error.StaticTypeWarningCode;
import com.google.dart.engine.error.StaticWarningCode;
import com.google.dart.engine.html.ast.HtmlUnit;
import com.google.dart.engine.html.ast.XmlAttributeNode;
import com.google.dart.engine.html.ast.XmlExpression;
import com.google.dart.engine.html.ast.XmlTagNode;
import com.google.dart.engine.html.ast.visitor.RecursiveXmlVisitor;
import com.google.dart.engine.html.parser.HtmlParser;
import com.google.dart.engine.internal.context.InternalAnalysisContext;
import com.google.dart.engine.internal.element.CompilationUnitElementImpl;
import com.google.dart.engine.internal.element.FunctionElementImpl;
import com.google.dart.engine.internal.element.HtmlElementImpl;
import com.google.dart.engine.internal.element.ImportElementImpl;
import com.google.dart.engine.internal.element.LibraryElementImpl;
import com.google.dart.engine.internal.element.LocalVariableElementImpl;
import com.google.dart.engine.internal.element.angular.AngularApplication;
import com.google.dart.engine.internal.element.angular.AngularComponentElementImpl;
import com.google.dart.engine.internal.element.angular.AngularElementImpl;
import com.google.dart.engine.internal.element.angular.AngularViewElementImpl;
import com.google.dart.engine.internal.resolver.InheritanceManager;
import com.google.dart.engine.internal.resolver.ResolverVisitor;
import com.google.dart.engine.internal.resolver.TypeProvider;
import com.google.dart.engine.internal.scope.Scope;
import com.google.dart.engine.parser.Parser;
import com.google.dart.engine.scanner.StringToken;
import com.google.dart.engine.scanner.Token;
import com.google.dart.engine.scanner.TokenType;
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.StringUtilities;
import com.google.dart.engine.utilities.source.LineInfo;

import static com.google.dart.engine.internal.html.angular.AngularMoustacheXmlExpression.CLOSING_DELIMITER_CHAR;
import static com.google.dart.engine.internal.html.angular.AngularMoustacheXmlExpression.CLOSING_DELIMITER_LENGTH;
import static com.google.dart.engine.internal.html.angular.AngularMoustacheXmlExpression.OPENING_DELIMITER_CHAR;
import static com.google.dart.engine.internal.html.angular.AngularMoustacheXmlExpression.OPENING_DELIMITER_LENGTH;

import java.net.URI;
import java.net.URISyntaxException;
import java.util.Collections;
import java.util.List;
import java.util.Set;

/**
* Instances of the class {@link AngularHtmlUnitResolver} resolve Angular specific expressions.
*/
public class AngularHtmlUnitResolver extends RecursiveXmlVisitor<Void> {
  private static class FilteringAnalysisErrorListener implements AnalysisErrorListener {
    private final AnalysisErrorListener listener;

    public FilteringAnalysisErrorListener(AnalysisErrorListener listener) {
      this.listener = listener;
    }

    @Override
    public void onError(AnalysisError error) {
      ErrorCode errorCode = error.getErrorCode();
      if (errorCode == StaticWarningCode.UNDEFINED_GETTER
          || errorCode == StaticWarningCode.UNDEFINED_IDENTIFIER
          || errorCode == StaticTypeWarningCode.UNDEFINED_GETTER) {
        return;
      }
      listener.onError(error);
    }
  }

  private static class FoundAppError extends Error {
  }

  private static final String NG_APP = "ng-app";

  /**
   * Checks if given {@link Element} is an artificial local variable and returns corresponding
   * {@link AngularElement}, or {@code null} otherwise.
   */
  public static AngularElement getAngularElement(Element element) {
    // may be artificial local variable, replace with AngularElement
    if (element instanceof LocalVariableElement) {
      LocalVariableElement local = (LocalVariableElement) element;
      ToolkitObjectElement[] toolkitObjects = local.getToolkitObjects();
      if (toolkitObjects.length == 1 && toolkitObjects[0] instanceof AngularElement) {
        return (AngularElement) toolkitObjects[0];
      }
    }
    // not a special Element
    return null;
  }

  /**
   * @return {@code true} if the given {@link HtmlUnit} has <code>ng-app</code> annotation.
   */
  public static boolean hasAngularAnnotation(HtmlUnit htmlUnit) {
    try {
      htmlUnit.accept(new RecursiveXmlVisitor<Void>() {
        @Override
        public Void visitXmlTagNode(XmlTagNode node) {
          if (node.getAttribute(NG_APP) != null) {
            throw new FoundAppError();
          }
          return super.visitXmlTagNode(node);
        }
      });
    } catch (FoundAppError e) {
      return true;
    }
    return false;
  }

  static SimpleIdentifier createIdentifier(String name, int offset) {
    StringToken token = createStringToken(name, offset);
    return new SimpleIdentifier(token);
  }

  /**
   * Adds {@link AngularElement} declared by the given top-level {@link Element}.
   *
   * @param angularElements the list to fill with top-level {@link AngularElement}s
   * @param classElement the {@link ClassElement} to get {@link AngularElement}s from
   */
  private static void addAngularElementsFromClass(Set<AngularElement> angularElements,
      ClassElement classElement) {
    for (ToolkitObjectElement toolkitObject : classElement.getToolkitObjects()) {
      if (toolkitObject instanceof AngularElement) {
        angularElements.add((AngularElement) toolkitObject);
      }
    }
  }

  /**
   * Returns the array of all top-level Angular elements that could be used in this library.
   *
   * @param libraryElement the {@link LibraryElement} to analyze
   * @return the array of all top-level Angular elements that could be used in this library
   */
  private static void addAngularElementsFromLibrary(Set<AngularElement> angularElements,
      LibraryElement library, Set<LibraryElement> visited) {
    if (library == null) {
      return;
    }
    if (!visited.add(library)) {
      return;
    }
    // add Angular elements from current library
    for (CompilationUnitElement unit : library.getUnits()) {
      Collections.addAll(angularElements, unit.getAngularViews());
      for (ClassElement type : unit.getTypes()) {
        addAngularElementsFromClass(angularElements, type);
      }
    }
    // handle imports
    for (ImportElement importElement : library.getImports()) {
      LibraryElement importedLibrary = importElement.getImportedLibrary();
      addAngularElementsFromLibrary(angularElements, importedLibrary, visited);
    }
  }

  // TODO(scheglov) rename to: createIdentifierToken
  private static StringToken createStringToken(String name, int offset) {
    return new StringToken(TokenType.IDENTIFIER, name, offset);
  }

  /**
   * Returns the array of all top-level Angular elements that could be used in this library.
   *
   * @param libraryElement the {@link LibraryElement} to analyze
   * @return the array of all top-level Angular elements that could be used in this library
   */
  private static AngularElement[] getAngularElements(Set<LibraryElement> libraries,
      LibraryElement libraryElement) {
    Set<AngularElement> angularElements = Sets.newHashSet();
    addAngularElementsFromLibrary(angularElements, libraryElement, libraries);
    return angularElements.toArray(new AngularElement[angularElements.size()]);
  }

  /**
   * Returns the external Dart {@link CompilationUnit} referenced by the given {@link HtmlUnit}.
   */
  private static CompilationUnit getDartUnit(AnalysisContext context, HtmlUnit unit)
      throws AnalysisException {
    for (HtmlScriptElement script : unit.getElement().getScripts()) {
      if (script instanceof ExternalHtmlScriptElement) {
        Source scriptSource = ((ExternalHtmlScriptElement) script).getScriptSource();
        if (scriptSource != null) {
          return context.resolveCompilationUnit(scriptSource, scriptSource);
        }
      }
    }
    return null;
  }

  private static Set<Source> getLibrarySources(Set<LibraryElement> libraries) {
    Set<Source> sources = Sets.newHashSet();
    for (LibraryElement library : libraries) {
      sources.add(library.getSource());
    }
    return sources;
  }

  private final InternalAnalysisContext context;
  private final TypeProvider typeProvider;
  private final FilteringAnalysisErrorListener errorListener;

  private final Source source;
  private final LineInfo lineInfo;
  private final HtmlUnit unit;
  private AngularElement[] angularElements;
  private final List<NgProcessor> processors = Lists.newArrayList();

  private LibraryElementImpl libraryElement;
  private CompilationUnitElementImpl unitElement;
  private FunctionElementImpl functionElement;
  private ResolverVisitor resolver;
  private boolean isAngular = false;

  private List<LocalVariableElementImpl> definedVariables = Lists.newArrayList();

  private Set<LibraryElement> injectedLibraries = Sets.newHashSet();

  private Scope topNameScope;

  private Scope nameScope;

  public AngularHtmlUnitResolver(InternalAnalysisContext context,
      AnalysisErrorListener errorListener, Source source, LineInfo lineInfo, HtmlUnit unit)
      throws AnalysisException {
    this.context = context;
    this.typeProvider = context.getTypeProvider();
    this.errorListener = new FilteringAnalysisErrorListener(errorListener);
    this.source = source;
    this.lineInfo = lineInfo;
    this.unit = unit;
  }

  /**
   * The {@link AngularApplication} for the Web application with this entry point, may be
   * {@code null} if not an entry point.
   */
  public AngularApplication calculateAngularApplication() throws AnalysisException {
    // check if Angular at all
    if (!hasAngularAnnotation(unit)) {
      return null;
    }
    // prepare resolved Dart unit
    CompilationUnit dartUnit = getDartUnit(context, unit);
    if (dartUnit == null) {
      return null;
    }
    // prepare accessible Angular elements
    LibraryElement libraryElement = dartUnit.getElement().getLibrary();
    Set<LibraryElement> libraries = Sets.newHashSet();
    AngularElement[] angularElements = getAngularElements(libraries, libraryElement);
    // resolve AngularComponentElement template URIs
    // TODO(scheglov) resolve to HtmlElement to allow F3 ?
    Set<Source> angularElementsSources = Sets.newHashSet();
    for (AngularElement angularElement : angularElements) {
      if (angularElement instanceof AngularHasTemplateElement) {
        AngularHasTemplateElement hasTemplate = (AngularHasTemplateElement) angularElement;
        angularElementsSources.add(angularElement.getSource());
        String templateUri = hasTemplate.getTemplateUri();
        if (templateUri == null) {
          continue;
        }
        try {
          Source templateSource = context.getSourceFactory().forUri(
              source.resolveRelativeUri(new URI(templateUri)));
          if (!context.exists(templateSource)) {
            templateSource = context.getSourceFactory().resolveUri(source, "package:" + templateUri);
            if (!context.exists(templateSource)) {
              errorListener.onError(new AnalysisError(
                  angularElement.getSource(),
                  hasTemplate.getTemplateUriOffset(),
                  templateUri.length(),
                  AngularCode.URI_DOES_NOT_EXIST,
                  templateUri));
              continue;
            }
          }
          if (!AnalysisEngine.isHtmlFileName(templateUri)) {
            continue;
          }
          if (hasTemplate instanceof AngularComponentElementImpl) {
            ((AngularComponentElementImpl) hasTemplate).setTemplateSource(templateSource);
          }
          if (hasTemplate instanceof AngularViewElementImpl) {
            ((AngularViewElementImpl) hasTemplate).setTemplateSource(templateSource);
          }
        } catch (URISyntaxException exception) {
          errorListener.onError(new AnalysisError(
              angularElement.getSource(),
              hasTemplate.getTemplateUriOffset(),
              templateUri.length(),
              AngularCode.INVALID_URI,
              templateUri));
        }
      }
    }
    // create AngularApplication
    AngularApplication application = new AngularApplication(
        source,
        getLibrarySources(libraries),
        angularElements,
        angularElementsSources.toArray(new Source[angularElementsSources.size()]));
    // set AngularApplication for each AngularElement
    for (AngularElement angularElement : angularElements) {
      ((AngularElementImpl) angularElement).setApplication(application);
    }
    // done
    return application;
  }

  /**
   * Resolves {@link #source} as an {@link AngularComponentElement} template file.
   *
   * @param application the Angular application we are resolving for
   * @param component the {@link AngularComponentElement} to resolve template for, not {@code null}
   */
  public void resolveComponentTemplate(AngularApplication application,
      AngularComponentElement component) throws AnalysisException {
    isAngular = true;
    resolveInternal(application.getElements(), component);
  }

  /**
   * Resolves {@link #source} as an Angular application entry point.
   */
  public void resolveEntryPoint(AngularApplication application) throws AnalysisException {
    resolveInternal(application.getElements(), null);
  }

  @Override
  public Void visitXmlAttributeNode(XmlAttributeNode node) {
    parseEmbeddedExpressionsInAttribute(node);
    resolveExpressions(node.getExpressions());
    return super.visitXmlAttributeNode(node);
  }

  @Override
  public Void visitXmlTagNode(XmlTagNode node) {
    boolean wasAngular = isAngular;
    try {
      // new Angular context
      if (node.getAttribute(NG_APP) != null) {
        isAngular = true;
        visitModelDirectives(node);
      }
      // not Angular
      if (!isAngular) {
        return super.visitXmlTagNode(node);
      }
      // process node in separate name scope
      pushNameScope();
      try {
        parseEmbeddedExpressionsInTag(node);
        // apply processors
        for (NgProcessor processor : processors) {
          if (processor.canApply(node)) {
            processor.apply(this, node);
          }
        }
        // resolve expressions
        resolveExpressions(node.getExpressions());
        // process children
        return super.visitXmlTagNode(node);
      } finally {
        popNameScope();
      }
    } finally {
      isAngular = wasAngular;
    }
  }

  /**
   * Creates new {@link LocalVariableElementImpl} with given type and identifier.
   *
   * @param type the {@link Type} of the variable
   * @param identifier the identifier to create variable for
   * @return the new {@link LocalVariableElementImpl}
   */
  LocalVariableElementImpl createLocalVariableFromIdentifier(Type type, SimpleIdentifier identifier) {
    LocalVariableElementImpl variable = new LocalVariableElementImpl(identifier);
    definedVariables.add(variable);
    variable.setType(type);
    return variable;
  }

  /**
   * Creates new {@link LocalVariableElementImpl} with given name and type.
   *
   * @param type the {@link Type} of the variable
   * @param name the name of the variable
   * @return the new {@link LocalVariableElementImpl}
   */
  LocalVariableElementImpl createLocalVariableWithName(Type type, String name) {
    SimpleIdentifier identifier = createIdentifier(name, 0);
    return createLocalVariableFromIdentifier(type, identifier);
  }

  /**
   * Declares the given {@link LocalVariableElementImpl} in the {@link #topNameScope}.
   */
  void defineTopVariable(LocalVariableElementImpl variable) {
    recordDefinedVariable(variable);
    topNameScope.define(variable);
    recordTypeLibraryInjected(variable);
  }

  /**
   * Declares the given {@link LocalVariableElementImpl} in the current {@link #nameScope}.
   */
  void defineVariable(LocalVariableElementImpl variable) {
    recordDefinedVariable(variable);
    nameScope.define(variable);
    recordTypeLibraryInjected(variable);
  }

  /**
   * @return the {@link AngularElement} with the given name, maybe {@code null}.
   */
  AngularElement findAngularElement(String name) {
    for (AngularElement element : angularElements) {
      if (name.equals(element.getName())) {
        return element;
      }
    }
    return null;
  }

  /**
   * @return the {@link TypeProvider} of the {@link AnalysisContext}.
   */
  TypeProvider getTypeProvider() {
    return typeProvider;
  }

  /**
   * Parses given {@link String} as an {@link AngularExpression} at the given offset.
   */
  AngularExpression parseAngularExpression(String contents, int startIndex, int endIndex, int offset) {
    Token token = scanDart(contents, startIndex, endIndex, offset);
    return parseAngularExpressionInToken(token);
  }

  AngularExpression parseAngularExpressionInToken(Token token) {
    List<Token> tokens = splitAtBar(token);
    Expression mainExpression = parseDartExpressionInToken(tokens.get(0));
    // parse formatters
    List<AngularFormatterNode> formatters = Lists.newArrayList();
    for (int i = 1; i < tokens.size(); i++) {
      Token formatterToken = tokens.get(i);
      Token barToken = formatterToken;
      formatterToken = formatterToken.getNext();
      // parse name
      Expression nameExpression = parseDartExpressionInToken(formatterToken);
      if (!(nameExpression instanceof SimpleIdentifier)) {
        reportErrorForNode(AngularCode.INVALID_FORMATTER_NAME, nameExpression);
        continue;
      }
      SimpleIdentifier name = (SimpleIdentifier) nameExpression;
      formatterToken = name.getEndToken().getNext();
      // parse arguments
      List<AngularFormatterArgument> arguments = Lists.newArrayList();
      while (formatterToken.getType() != TokenType.EOF) {
        // skip ":"
        Token colonToken = formatterToken;
        if (colonToken.getType() == TokenType.COLON) {
          formatterToken = formatterToken.getNext();
        } else {
          reportErrorForToken(AngularCode.MISSING_FORMATTER_COLON, colonToken);
        }
        // parse argument
        Expression argument = parseDartExpressionInToken(formatterToken);
        arguments.add(new AngularFormatterArgument(colonToken, argument));
        // next token
        formatterToken = argument.getEndToken().getNext();
      }
      formatters.add(new AngularFormatterNode(barToken, name, arguments));
    }
    // done
    return new AngularExpression(mainExpression, formatters);
  }

  /**
   * Parses given {@link String} as an {@link Expression} at the given offset.
   */
  Expression parseDartExpression(String contents, int startIndex, int endIndex, int offset) {
    Token token = scanDart(contents, startIndex, endIndex, offset);
    return parseDartExpressionInToken(token);
  }

  Expression parseDartExpressionInToken(Token token) {
    Parser parser = new Parser(source, errorListener);
    return parser.parseExpression(token);
  }

  void popNameScope() {
    nameScope = resolver.popNameScope();
  }

  void pushNameScope() {
    nameScope = resolver.pushNameScope();
  }

  /**
   * Reports given {@link ErrorCode} at the given {@link AstNode}.
   */
  void reportErrorForNode(ErrorCode errorCode, AstNode node, Object... arguments) {
    reportErrorForOffset(errorCode, node.getOffset(), node.getLength(), arguments);
  }

  /**
   * Reports given {@link ErrorCode} at the given position.
   */
  void reportErrorForOffset(ErrorCode errorCode, int offset, int length, Object... arguments) {
    errorListener.onError(new AnalysisError(source, offset, length, errorCode, arguments));
  }

  /**
   * Reports given {@link ErrorCode} at the given {@link Token}.
   */
  void reportErrorForToken(ErrorCode errorCode, Token token, Object... arguments) {
    reportErrorForOffset(errorCode, token.getOffset(), token.getLength(), arguments);
  }

  void resolveExpression(AngularExpression angularExpression) {
    List<Expression> dartExpressions = angularExpression.getExpressions();
    for (Expression dartExpression : dartExpressions) {
      resolveNode(dartExpression);
    }
  }

  /**
   * Resolves given {@link AstNode} using {@link #resolver}.
   */
  void resolveNode(AstNode node) {
    node.accept(resolver);
  }

  Token scanDart(String contents, int startIndex, int endIndex, int offset) {
    return HtmlParser.scanDartSource(
        source,
        lineInfo,
        contents.substring(startIndex, endIndex),
        offset + startIndex,
        errorListener);
  }

  /**
   * Puts into {@link #libraryElement} an artificial {@link LibraryElementImpl} for this HTML
   * {@link Source}.
   */
  private void createLibraryElement() {
    // create CompilationUnitElementImpl
    String unitName = source.getShortName();
    unitElement = new CompilationUnitElementImpl(unitName);
    unitElement.setSource(source);
    // create LibraryElementImpl
    libraryElement = new LibraryElementImpl(context.getContextFor(source), null);
    libraryElement.setDefiningCompilationUnit(unitElement);
    libraryElement.setAngularHtml(true);
    injectedLibraries.add(libraryElement);
    // create FunctionElementImpl
    functionElement = new FunctionElementImpl(0);
    unitElement.setFunctions(new FunctionElement[] {functionElement});
  }

  /**
   * Creates new {@link NgProcessor} for the given {@link AngularElement}, maybe {@code null} if not
   * supported.
   */
  private NgProcessor createProcessor(AngularElement element) {
    if (element instanceof AngularComponentElement) {
      AngularComponentElement component = (AngularComponentElement) element;
      return new NgComponentElementProcessor(component);
    }
    if (element instanceof AngularControllerElement) {
      AngularControllerElement controller = (AngularControllerElement) element;
      return new NgControllerElementProcessor(controller);
    }
    if (element instanceof AngularDecoratorElement) {
      AngularDecoratorElement directive = (AngularDecoratorElement) element;
      return new NgDecoratorElementProcessor(directive);
    }
    return null;
  }

  /**
   * Puts into {@link #resolver} an {@link ResolverVisitor} to resolve {@link Expression}s in
   * {@link #source}.
   */
  private void createResolver() {
    InheritanceManager inheritanceManager = new InheritanceManager(libraryElement);
    resolver = new ResolverVisitor(
        libraryElement,
        source,
        typeProvider,
        inheritanceManager,
        errorListener);
    topNameScope = resolver.pushNameScope();
    // add Scope variables - no type, no location, just to avoid warnings
    {
      Type type = typeProvider.getDynamicType();
      topNameScope.define(createLocalVariableWithName(type, "$id"));
      topNameScope.define(createLocalVariableWithName(type, "$parent"));
      topNameScope.define(createLocalVariableWithName(type, "$root"));
    }
  }

  /**
   * Defines variable for the given {@link AngularElement} with type of the enclosing
   * {@link ClassElement}.
   */
  private void defineTopVariable_forClassElement(AngularElement element) {
    ClassElement classElement = (ClassElement) element.getEnclosingElement();
    InterfaceType type = classElement.getType();
    LocalVariableElementImpl variable = createLocalVariableWithName(type, element.getName());
    defineTopVariable(variable);
    variable.setToolkitObjects(new AngularElement[] {element});
  }

  /**
   * Defines variable for the given {@link AngularScopePropertyElement}.
   */
  private void defineTopVariable_forScopeProperty(AngularScopePropertyElement element) {
    Type type = element.getType();
    LocalVariableElementImpl variable = createLocalVariableWithName(type, element.getName());
    defineTopVariable(variable);
    variable.setToolkitObjects(new AngularElement[] {element});
  }

  /**
   * 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 parseEmbeddedExpressions(List<AngularMoustacheXmlExpression> expressions,
      com.google.dart.engine.html.scanner.Token token) {
    // prepare Token information
    String lexeme = token.getLexeme();
    int offset = token.getOffset();
    // find expressions between {{ and }}
    int startIndex = StringUtilities.indexOf2(
        lexeme,
        0,
        OPENING_DELIMITER_CHAR,
        OPENING_DELIMITER_CHAR);
    while (startIndex >= 0) {
      int endIndex = StringUtilities.indexOf2(
          lexeme,
          startIndex + OPENING_DELIMITER_LENGTH,
          CLOSING_DELIMITER_CHAR,
          CLOSING_DELIMITER_CHAR);
      if (endIndex < 0) {
        // TODO(brianwilkerson) Should we report this error or will it be reported by something else?
        return;
      } else if (startIndex + OPENING_DELIMITER_LENGTH < endIndex) {
        startIndex += OPENING_DELIMITER_LENGTH;
        AngularExpression expression = parseAngularExpression(lexeme, startIndex, endIndex, offset);
        expressions.add(new AngularMoustacheXmlExpression(startIndex, endIndex, expression));
      }
      startIndex = StringUtilities.indexOf2(
          lexeme,
          endIndex + CLOSING_DELIMITER_LENGTH,
          OPENING_DELIMITER_CHAR,
          OPENING_DELIMITER_CHAR);
    }
  }

  private void parseEmbeddedExpressionsInAttribute(XmlAttributeNode node) {
    List<AngularMoustacheXmlExpression> expressions = Lists.newArrayList();
    parseEmbeddedExpressions(expressions, node.getValueToken());
    if (!expressions.isEmpty()) {
      node.setExpressions(expressions.toArray(new AngularMoustacheXmlExpression[expressions.size()]));
    }
  }

  private void parseEmbeddedExpressionsInTag(XmlTagNode node) {
    List<AngularMoustacheXmlExpression> expressions = Lists.newArrayList();
    com.google.dart.engine.html.scanner.Token token = node.getAttributeEnd();
    com.google.dart.engine.html.scanner.Token endToken = node.getEndToken();
    boolean inChild = false;
    while (token != endToken) {
      for (XmlTagNode child : node.getTagNodes()) {
        if (token == child.getBeginToken()) {
          inChild = true;
          break;
        }
        if (token == child.getEndToken()) {
          inChild = false;
          break;
        }
      }
      if (!inChild && token.getType() == com.google.dart.engine.html.scanner.TokenType.TEXT) {
        parseEmbeddedExpressions(expressions, token);
      }
      token = token.getNext();
    }
    node.setExpressions(expressions.toArray(new AngularMoustacheXmlExpression[expressions.size()]));
  }

  private void recordDefinedVariable(LocalVariableElementImpl variable) {
    definedVariables.add(variable);
    functionElement.setLocalVariables(definedVariables.toArray(new LocalVariableElementImpl[definedVariables.size()]));
  }

  /**
   * When we inject variable, we give access to the library of its type.
   */
  private void recordTypeLibraryInjected(LocalVariableElementImpl variable) {
    LibraryElement typeLibrary = variable.getType().getElement().getLibrary();
    injectedLibraries.add(typeLibrary);
  }

  private void resolveExpressions(XmlExpression[] expressions) {
    for (XmlExpression xmlExpression : expressions) {
      if (xmlExpression instanceof AngularXmlExpression) {
        AngularXmlExpression angularXmlExpression = (AngularXmlExpression) xmlExpression;
        resolveXmlExpression(angularXmlExpression);
      }
    }
  }

  /**
   * Resolves Angular specific expressions and elements in the {@link #source}.
   *
   * @param angularElements the {@link AngularElement}s accessible in the component's library, not
   *          {@code null}
   * @param component the {@link AngularComponentElement} to resolve template for, maybe
   *          {@code null} if not a component template
   */
  private void resolveInternal(AngularElement[] angularElements, AngularComponentElement component)
      throws AnalysisException {
    this.angularElements = angularElements;
    // add built-in processors
    processors.add(NgModelProcessor.INSTANCE);
    processors.add(NgRepeatProcessor.INSTANCE);
    // add element's libraries
    for (AngularElement angularElement : angularElements) {
      injectedLibraries.add(angularElement.getLibrary());
    }
    // prepare Dart library
    createLibraryElement();
    ((HtmlElementImpl) unit.getElement()).setAngularCompilationUnit(unitElement);
    // prepare Dart resolver
    createResolver();
    // maybe resolving component template
    if (component != null) {
      defineTopVariable_forClassElement(component);
      for (AngularScopePropertyElement scopeProperty : component.getScopeProperties()) {
        defineTopVariable_forScopeProperty(scopeProperty);
      }
    }
    // add processors
    for (AngularElement angularElement : angularElements) {
      NgProcessor processor = createProcessor(angularElement);
      if (processor != null) {
        processors.add(processor);
      }
    }
    // define formatters
    for (AngularElement angularElement : angularElements) {
      if (angularElement instanceof AngularFormatterElement) {
        defineTopVariable_forClassElement(angularElement);
      }
    }
    // run this HTML visitor
    unit.accept(this);
    // simulate imports for injects
    {
      List<ImportElement> imports = Lists.newArrayList();
      for (LibraryElement injectedLibrary : injectedLibraries) {
        ImportElementImpl importElement = new ImportElementImpl(-1);
        importElement.setImportedLibrary(injectedLibrary);
        imports.add(importElement);
      }
      libraryElement.setImports(imports.toArray(new ImportElement[imports.size()]));
    }
  }

  private void resolveXmlExpression(AngularXmlExpression angularXmlExpression) {
    AngularExpression angularExpression = angularXmlExpression.getExpression();
    resolveExpression(angularExpression);
  }

  // XXX
  private List<Token> splitAtBar(Token token) {
    List<Token> tokens = Lists.newArrayList();
    tokens.add(token);
    while (token.getType() != TokenType.EOF) {
      if (token.getType() == TokenType.BAR) {
        tokens.add(token);
        Token eofToken = new Token(TokenType.EOF, 0);
        token.getPrevious().setNext(eofToken);
      }
      token = token.getNext();
    }
    return tokens;
  }

  /**
   * The "ng-model" directive is special, it contributes to the top-level name scope. These models
   * can be used before actual "ng-model" attribute in HTML. So, we need to define them once we
   * found {@link #NG_APP} context.
   */
  private void visitModelDirectives(XmlTagNode appNode) {
    appNode.accept(new RecursiveXmlVisitor<Void>() {
      @Override
      public Void visitXmlTagNode(XmlTagNode node) {
        NgModelProcessor directive = NgModelProcessor.INSTANCE;
        if (directive.canApply(node)) {
          directive.applyTopDeclarations(AngularHtmlUnitResolver.this, node);
        }
        return super.visitXmlTagNode(node);
      }
    });
  }
}
TOP

Related Classes of com.google.dart.engine.internal.html.angular.AngularHtmlUnitResolver$FoundAppError

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.