/*
* 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.task;
import com.google.dart.engine.ast.CompilationUnit;
import com.google.dart.engine.ast.Directive;
import com.google.dart.engine.ast.UriBasedDirective;
import com.google.dart.engine.context.AnalysisContext;
import com.google.dart.engine.context.AnalysisException;
import com.google.dart.engine.error.AnalysisError;
import com.google.dart.engine.error.AnalysisErrorListener;
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.visitor.RecursiveXmlVisitor;
import com.google.dart.engine.html.parser.HtmlParser;
import com.google.dart.engine.html.scanner.AbstractScanner;
import com.google.dart.engine.html.scanner.StringScanner;
import com.google.dart.engine.html.scanner.Token;
import com.google.dart.engine.internal.context.InternalAnalysisContext;
import com.google.dart.engine.internal.context.RecordingErrorListener;
import com.google.dart.engine.source.Source;
import com.google.dart.engine.utilities.source.LineInfo;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
/**
* Instances of the class {@code ParseHtmlTask} parse a specific source as an HTML file.
*/
public class ParseHtmlTask extends AnalysisTask {
/**
* The source to be parsed.
*/
private Source source;
/**
* The time at which the contents of the source were last modified.
*/
private long modificationTime;
/**
* The contents of the source.
*/
private CharSequence content;
/**
* The line information that was produced.
*/
private LineInfo lineInfo;
/**
* The HTML unit that was produced by parsing the source.
*/
private HtmlUnit unit;
/**
* The errors that were produced by scanning and parsing the source.
*/
private AnalysisError[] errors = AnalysisError.NO_ERRORS;
/**
* An array containing the sources of the libraries that are referenced within the HTML.
*/
private Source[] referencedLibraries = Source.EMPTY_ARRAY;
/**
* The name of the 'src' attribute in a HTML tag.
*/
private static final String ATTRIBUTE_SRC = "src";
/**
* The name of the 'script' tag in an HTML file.
*/
private static final String TAG_SCRIPT = "script";
/**
* Initialize a newly created task to perform analysis within the given context.
*
* @param context the context in which the task is to be performed
* @param source the source to be parsed
* @param modificationTime the time at which the contents of the source were last modified
* @param content the contents of the source
*/
public ParseHtmlTask(InternalAnalysisContext context, Source source, long modificationTime,
CharSequence content) {
super(context);
this.source = source;
this.modificationTime = modificationTime;
this.content = content;
}
@Override
public <E> E accept(AnalysisTaskVisitor<E> visitor) throws AnalysisException {
return visitor.visitParseHtmlTask(this);
}
/**
* Return the errors that were produced by scanning and parsing the source, or {@code null} if the
* task has not yet been performed or if an exception occurred.
*
* @return the errors that were produced by scanning and parsing the source
*/
public AnalysisError[] getErrors() {
return errors;
}
/**
* Return the HTML unit that was produced by parsing the source.
*
* @return the HTML unit that was produced by parsing the source
*/
public HtmlUnit getHtmlUnit() {
return unit;
}
/**
* Return the line information that was produced, or {@code null} if the task has not yet been
* performed or if an exception occurred.
*
* @return the line information that was produced
*/
public LineInfo getLineInfo() {
return lineInfo;
}
/**
* Return the time at which the contents of the source that was parsed were last modified, or a
* negative value if the task has not yet been performed or if an exception occurred.
*
* @return the time at which the contents of the source that was parsed were last modified
*/
public long getModificationTime() {
return modificationTime;
}
/**
* Return an array containing the sources of the libraries that are referenced within the HTML.
*
* @return the sources of the libraries that are referenced within the HTML
*/
public Source[] getReferencedLibraries() {
return referencedLibraries;
}
/**
* Return the source that was or is to be parsed.
*
* @return the source was or is to be parsed
*/
public Source getSource() {
return source;
}
@Override
protected String getTaskDescription() {
if (source == null) {
return "parse as html null source";
}
return "parse as html " + source.getFullName();
}
@Override
protected void internalPerform() throws AnalysisException {
try {
AbstractScanner scanner = new StringScanner(source, content);
scanner.setPassThroughElements(new String[] {TAG_SCRIPT});
Token token = scanner.tokenize();
lineInfo = new LineInfo(scanner.getLineStarts());
final RecordingErrorListener errorListener = new RecordingErrorListener();
unit = new HtmlParser(source, errorListener).parse(token, lineInfo);
unit.accept(new RecursiveXmlVisitor<Void>() {
@Override
public Void visitHtmlScriptTagNode(HtmlScriptTagNode node) {
resolveScriptDirectives(node.getScript(), errorListener);
return null;
}
});
errors = errorListener.getErrorsForSource(source);
referencedLibraries = getLibrarySources();
} catch (Exception exception) {
throw new AnalysisException("Exception", exception);
}
}
/**
* Return the sources of libraries that are referenced in the specified HTML file.
*
* @return the sources of libraries that are referenced in the HTML file
*/
private Source[] getLibrarySources() {
final ArrayList<Source> libraries = new ArrayList<Source>();
unit.accept(new RecursiveXmlVisitor<Void>() {
@Override
public Void visitHtmlScriptTagNode(HtmlScriptTagNode node) {
XmlAttributeNode scriptAttribute = null;
for (XmlAttributeNode attribute : node.getAttributes()) {
if (attribute.getName().equalsIgnoreCase(ATTRIBUTE_SRC)) {
scriptAttribute = attribute;
}
}
if (scriptAttribute != null) {
try {
URI uri = new URI(null, null, scriptAttribute.getText(), null);
String fileName = uri.getPath();
Source librarySource = getContext().getSourceFactory().resolveUri(source, fileName);
if (getContext().exists(librarySource)) {
libraries.add(librarySource);
}
} catch (URISyntaxException e) {
// ignored - invalid URI reported during resolution phase
}
}
return super.visitHtmlScriptTagNode(node);
}
});
if (libraries.isEmpty()) {
return Source.EMPTY_ARRAY;
}
return libraries.toArray(new Source[libraries.size()]);
}
/**
* Resolves directives in the given {@link CompilationUnit}.
*/
private void resolveScriptDirectives(CompilationUnit script, AnalysisErrorListener errorListener) {
if (script == null) {
return;
}
AnalysisContext analysisContext = getContext();
for (Directive directive : script.getDirectives()) {
if (directive instanceof UriBasedDirective) {
ParseDartTask.resolveDirective(
analysisContext,
source,
(UriBasedDirective) directive,
errorListener);
}
}
}
}