Package com.google.dart.engine.internal.builder

Source Code of com.google.dart.engine.internal.builder.HtmlUnitBuilder

/*
* 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.builder;

import com.google.dart.engine.AnalysisEngine;
import com.google.dart.engine.context.AnalysisException;
import com.google.dart.engine.element.HtmlScriptElement;
import com.google.dart.engine.error.AnalysisError;
import com.google.dart.engine.error.ErrorCode;
import com.google.dart.engine.error.HtmlWarningCode;
import com.google.dart.engine.html.ast.HtmlScriptTagNode;
import com.google.dart.engine.html.ast.HtmlUnit;
import com.google.dart.engine.html.ast.XmlAttributeNode;
import com.google.dart.engine.html.ast.XmlTagNode;
import com.google.dart.engine.html.ast.visitor.XmlVisitor;
import com.google.dart.engine.html.scanner.TokenType;
import com.google.dart.engine.internal.context.InternalAnalysisContext;
import com.google.dart.engine.internal.context.RecordingErrorListener;
import com.google.dart.engine.internal.element.EmbeddedHtmlScriptElementImpl;
import com.google.dart.engine.internal.element.ExternalHtmlScriptElementImpl;
import com.google.dart.engine.internal.element.HtmlElementImpl;
import com.google.dart.engine.internal.element.LibraryElementImpl;
import com.google.dart.engine.internal.resolver.Library;
import com.google.dart.engine.internal.resolver.LibraryResolver;
import com.google.dart.engine.source.Source;
import com.google.dart.engine.utilities.io.UriUtilities;

import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;

/**
* Instances of the class {@code HtmlUnitBuilder} build an element model for a single HTML unit.
*/
public class HtmlUnitBuilder implements XmlVisitor<Void> {
  private static final String SRC = "src";

  /**
   * The analysis context in which the element model will be built.
   */
  private final InternalAnalysisContext context;

  /**
   * The error listener to which errors will be reported.
   */
  private RecordingErrorListener errorListener;

  /**
   * The modification time of the source for which an element is being built.
   */
  private long modificationStamp;

  /**
   * The HTML element being built.
   */
  private HtmlElementImpl htmlElement;

  /**
   * The elements in the path from the HTML unit to the current tag node.
   */
  private ArrayList<XmlTagNode> parentNodes;

  /**
   * The script elements being built.
   */
  private ArrayList<HtmlScriptElement> scripts;

  /**
   * A set of the libraries that were resolved while resolving the HTML unit.
   */
  private Set<Library> resolvedLibraries = new HashSet<Library>();

  /**
   * Initialize a newly created HTML unit builder.
   *
   * @param context the analysis context in which the element model will be built
   */
  public HtmlUnitBuilder(InternalAnalysisContext context) {
    this.context = context;
    this.errorListener = new RecordingErrorListener();
  }

  /**
   * Build the HTML element for the given source.
   *
   * @param source the source describing the compilation unit
   * @param modificationStamp the modification time of the source for which an element is being
   *          built
   * @param unit the AST structure representing the HTML
   * @throws AnalysisException if the analysis could not be performed
   */
  public HtmlElementImpl buildHtmlElement(Source source, long modificationStamp, HtmlUnit unit)
      throws AnalysisException {
    this.modificationStamp = modificationStamp;
    HtmlElementImpl result = new HtmlElementImpl(context, source.getShortName());
    result.setSource(source);
    htmlElement = result;
    unit.accept(this);
    htmlElement = null;
    unit.setElement(result);
    return result;
  }

  /**
   * Return the listener to which analysis errors will be reported.
   *
   * @return the listener to which analysis errors will be reported
   */
  public RecordingErrorListener getErrorListener() {
    return errorListener;
  }

  /**
   * Return an array containing information about all of the libraries that were resolved.
   *
   * @return an array containing the libraries that were resolved
   */
  public Set<Library> getResolvedLibraries() {
    return resolvedLibraries;
  }

  @Override
  public Void visitHtmlScriptTagNode(HtmlScriptTagNode node) {
    if (parentNodes.contains(node)) {
      return reportCircularity(node);
    }
    parentNodes.add(node);
    try {
      Source htmlSource = htmlElement.getSource();
      XmlAttributeNode scriptAttribute = getScriptSourcePath(node);
      String scriptSourcePath = scriptAttribute == null ? null : scriptAttribute.getText();
      if (node.getAttributeEnd().getType() == TokenType.GT && scriptSourcePath == null) {
        EmbeddedHtmlScriptElementImpl script = new EmbeddedHtmlScriptElementImpl(node);
        try {
          LibraryResolver resolver = new LibraryResolver(context);
          LibraryElementImpl library = (LibraryElementImpl) resolver.resolveEmbeddedLibrary(
              htmlSource,
              modificationStamp,
              node.getScript(),
              true);
          script.setScriptLibrary(library);
          resolvedLibraries.addAll(resolver.getResolvedLibraries());
          errorListener.addAll(resolver.getErrorListener());
        } catch (AnalysisException exception) {
          //TODO (danrubel): Handle or forward the exception
          AnalysisEngine.getInstance().getLogger().logError(
              "Could not resolve script tag",
              exception);
        }
        node.setScriptElement(script);
        scripts.add(script);
      } else {
        ExternalHtmlScriptElementImpl script = new ExternalHtmlScriptElementImpl(node);
        if (scriptSourcePath != null) {
          try {
            scriptSourcePath = UriUtilities.encode(scriptSourcePath);
            // Force an exception to be thrown if the URI is invalid so that we can report the
            // problem.
            new URI(scriptSourcePath);
            Source scriptSource = context.getSourceFactory().resolveUri(
                htmlSource,
                scriptSourcePath);
            script.setScriptSource(scriptSource);
            if (!context.exists(scriptSource)) {
              reportValueError(
                  HtmlWarningCode.URI_DOES_NOT_EXIST,
                  scriptAttribute,
                  scriptSourcePath);
            }
          } catch (URISyntaxException exception) {
            reportValueError(HtmlWarningCode.INVALID_URI, scriptAttribute, scriptSourcePath);
          }
        }
        node.setScriptElement(script);
        scripts.add(script);
      }
    } finally {
      parentNodes.remove(node);
    }
    return null;
  }

  @Override
  public Void visitHtmlUnit(HtmlUnit node) {
    parentNodes = new ArrayList<XmlTagNode>();
    scripts = new ArrayList<HtmlScriptElement>();
    try {
      node.visitChildren(this);
      htmlElement.setScripts(scripts.toArray(new HtmlScriptElement[scripts.size()]));
    } finally {
      scripts = null;
      parentNodes = null;
    }
    return null;
  }

  @Override
  public Void visitXmlAttributeNode(XmlAttributeNode node) {
    return null;
  }

  @Override
  public Void visitXmlTagNode(XmlTagNode node) {
    if (parentNodes.contains(node)) {
      return reportCircularity(node);
    }
    parentNodes.add(node);
    try {
      node.visitChildren(this);
    } finally {
      parentNodes.remove(node);
    }
    return null;
  }

  /**
   * Return the first source attribute for the given tag node, or {@code null} if it does not exist.
   *
   * @param node the node containing attributes
   * @return the source attribute contained in the given tag
   */
  private XmlAttributeNode getScriptSourcePath(XmlTagNode node) {
    for (XmlAttributeNode attribute : node.getAttributes()) {
      if (attribute.getName().equals(SRC)) {
        return attribute;
      }
    }
    return null;
  }

  private Void reportCircularity(XmlTagNode node) {
    //
    // This should not be possible, but we have an error report that suggests that it happened at
    // least once. This code will guard against infinite recursion and might help us identify the
    // cause of the issue.
    //
    StringBuilder builder = new StringBuilder();
    builder.append("Found circularity in XML nodes: ");
    boolean first = true;
    for (XmlTagNode pathNode : parentNodes) {
      if (first) {
        first = false;
      } else {
        builder.append(", ");
      }
      String tagName = pathNode.getTag();
      if (pathNode == node) {
        builder.append("*");
        builder.append(tagName);
        builder.append("*");
      } else {
        builder.append(tagName);
      }
    }
    AnalysisEngine.getInstance().getLogger().logError(builder.toString());
    return null;
  }

  /**
   * Report an error with the given error code at the given location. Use the given arguments to
   * compose the error message.
   *
   * @param errorCode the error code of the error to be reported
   * @param offset the offset of the first character to be highlighted
   * @param length the number of characters to be highlighted
   * @param arguments the arguments used to compose the error message
   */
  private void reportErrorForOffset(ErrorCode errorCode, int offset, int length,
      Object... arguments) {
    errorListener.onError(new AnalysisError(
        htmlElement.getSource(),
        offset,
        length,
        errorCode,
        arguments));
  }

  /**
   * Report an error with the given error code at the location of the value of the given attribute.
   * Use the given arguments to compose the error message.
   *
   * @param errorCode the error code of the error to be reported
   * @param offset the offset of the first character to be highlighted
   * @param length the number of characters to be highlighted
   * @param arguments the arguments used to compose the error message
   */
  private void reportValueError(ErrorCode errorCode, XmlAttributeNode attribute,
      Object... arguments) {
    int offset = attribute.getValueToken().getOffset() + 1;
    int length = attribute.getValueToken().getLength() - 2;
    reportErrorForOffset(errorCode, offset, length, arguments);
  }
}
TOP

Related Classes of com.google.dart.engine.internal.builder.HtmlUnitBuilder

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.