/* * Copyright 2012 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.tools.core.html; import com.google.common.base.Charsets; import com.google.common.io.Files; import com.google.dart.tools.core.DartCore; import com.google.dart.tools.core.DartCoreDebug; import com.google.dart.tools.core.builder.BuildEvent; import com.google.dart.tools.core.builder.BuildParticipant; import com.google.dart.tools.core.builder.BuildVisitor; import com.google.dart.tools.core.builder.CleanEvent; import com.google.dart.tools.core.internal.builder.MarkerUtilities; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IResourceDelta; import org.eclipse.core.resources.IResourceProxy; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import java.io.IOException; import java.util.List; /** * A build participant to analyze html files. * * @coverage dart.tools.core */ public class HtmlBuildParticipant implements BuildParticipant, BuildVisitor { @Override public void build(BuildEvent event, IProgressMonitor monitor) throws CoreException { event.traverse(this, false); } @Override public void clean(CleanEvent event, IProgressMonitor monitor) throws CoreException { MarkerUtilities.deleteMarkers(event.getProject()); } @Override public boolean visit(IResourceDelta delta, IProgressMonitor monitor) throws CoreException { if (delta.getKind() == IResourceDelta.CHANGED) { IResource resource = delta.getResource(); if (resource.getType() == IResource.FILE) { if (DartCore.isHtmlLikeFileName(resource.getName())) { processHtml((IFile) resource); } } } return true; } @Override public boolean visit(IResourceProxy proxy, IProgressMonitor monitor) throws CoreException { if (proxy.getType() == IResource.FILE) { if (DartCore.isHtmlLikeFileName(proxy.getName())) { processHtml((IFile) proxy.requestResource()); } } return true; } protected void processHtml(IFile file) { try { MarkerUtilities.deleteMarkers(file); XmlDocument document = new HtmlParser(Files.toString( file.getLocation().toFile(), Charsets.UTF_8)).parse(); validate(document, file); } catch (CoreException e) { DartCore.logError(e); } catch (IOException ioe) { } } private void validate(XmlDocument document, IFile file) throws CoreException { for (XmlNode node : document.getChildren()) { if (node instanceof XmlElement) { validate((XmlElement) node, file); } } } private void validate(XmlElement node, IFile file) throws CoreException { // We need to be very smart about html attribute validation, as lots of html code // uses older attributes, or makes up their own custom attributes. // We should probably look for typos of the common attributes. if (DartCoreDebug.ENABLE_HTML_VALIDATION) { if (node.getAttributes().size() > 0) { List<String> validAttributes = HtmlKeywords.getAttributes(node.getLabel()); if (!validAttributes.isEmpty()) { for (XmlAttribute attribute : node.getAttributes()) { String name = attribute.getName(); // A common pattern with data-binding frameworks. if (name.startsWith("ng-")) { continue; } // A data attribute. if (name.startsWith("data-")) { continue; } if (!validAttributes.contains(name)) { if (!HtmlKeywords.isValidEventAttribute(name)) { int charStart = attribute.getStartToken().getLocation(); String message = "\"" + name + "\" is not a valid attribute for the " + "<" + node.getLabel() + "> element."; MarkerUtilities.createWarningMarker( file, message, attribute.getStartToken().getLineNumber(), charStart, charStart + name.length()); } } } } } } for (XmlNode child : node.getChildren()) { if (child instanceof XmlElement) { validate((XmlElement) child, file); } } } }