summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorvrubezhny2014-01-29 15:22:48 (EST)
committerNick Sandonato2014-01-29 16:17:50 (EST)
commitf4f3fd5fd74846be7ef6ae0caa851d9b8e1e163b (patch)
treeea7aa7a50fe951c2dfad9181d98282a8a1837af8
parent7eeb69e21bca70e34cc78af749d0f48c5dc7e980 (diff)
downloadwebtools.sourceediting-f4f3fd5fd74846be7ef6ae0caa851d9b8e1e163b.zip
webtools.sourceediting-f4f3fd5fd74846be7ef6ae0caa851d9b8e1e163b.tar.gz
webtools.sourceediting-f4f3fd5fd74846be7ef6ae0caa851d9b8e1e163b.tar.bz2
HTML5 attribute validator marks ng-app AngularJS attributes as undefinedv201401292150
A new preferences are created for the pattern of attribute names to be ignored by HTMLAttributeValidator The preference initializer is made to initialize new preferences HTMLAttributeValidator is made to understand attribute names ignorance preferences Signed-off-by: vrubezhny <vrubezhny@exadel.com>
-rw-r--r--bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/internal/preferences/HTMLCorePreferenceInitializer.java8
-rw-r--r--bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/internal/preferences/HTMLCorePreferenceNames.java22
-rw-r--r--bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/internal/validate/HTMLAttributeValidator.java91
-rw-r--r--bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/internal/validate/StringMatcher.java232
4 files changed, 344 insertions, 9 deletions
diff --git a/bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/internal/preferences/HTMLCorePreferenceInitializer.java b/bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/internal/preferences/HTMLCorePreferenceInitializer.java
index 6eb852a..207744d 100644
--- a/bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/internal/preferences/HTMLCorePreferenceInitializer.java
+++ b/bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/internal/preferences/HTMLCorePreferenceInitializer.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2005, 2012 IBM Corporation and others.
+ * Copyright (c) 2005, 2014 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -7,6 +7,8 @@
*
* Contributors:
* IBM Corporation - initial API and implementation
+ * Red Hat, Inc. - Bug #426939 - [validator] HTML5 attribute validator
+ * marks ng-app AngularJS attributes as undefined
*******************************************************************************/
package org.eclipse.wst.html.core.internal.preferences;
@@ -72,6 +74,10 @@ public class HTMLCorePreferenceInitializer extends AbstractPreferenceInitializer
* @param node the Eclipse preference node
*/
private void initializeValidationPreferences(IEclipsePreferences node) {
+ // Ignored Attribute names
+ node.putBoolean(HTMLCorePreferenceNames.IGNORE_ATTRIBUTE_NAMES, HTMLCorePreferenceNames.IGNORE_ATTRIBUTE_NAMES_DEFAULT);
+ node.put(HTMLCorePreferenceNames.ATTRIBUTE_NAMES_TO_IGNORE, HTMLCorePreferenceNames.ATTRIBUTE_NAMES_TO_IGNORE_DEFAULT);
+
// Attributes
node.putInt(HTMLCorePreferenceNames.ATTRIBUTE_UNDEFINED_NAME, ValidationMessage.WARNING);
node.putInt(HTMLCorePreferenceNames.ATTRIBUTE_UNDEFINED_VALUE, ValidationMessage.WARNING);
diff --git a/bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/internal/preferences/HTMLCorePreferenceNames.java b/bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/internal/preferences/HTMLCorePreferenceNames.java
index fe7fb60..6cdc909 100644
--- a/bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/internal/preferences/HTMLCorePreferenceNames.java
+++ b/bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/internal/preferences/HTMLCorePreferenceNames.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2005, 2011 IBM Corporation and others.
+ * Copyright (c) 2005, 2014 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -8,7 +8,8 @@
* Contributors:
* IBM Corporation - initial API and implementation
* Jens Lukowski/Innoopract - initial renaming/restructuring
- *
+ * Red Hat, Inc. - Bug #426939 - [validator] HTML5 attribute validator
+ * marks ng-app AngularJS attributes as undefined
*******************************************************************************/
package org.eclipse.wst.html.core.internal.preferences;
@@ -238,7 +239,23 @@ public class HTMLCorePreferenceNames {
*/
public static final int UPPER = 2;
+ /**
+ * Default value for the preference #IGNORE_ATTRIBUTE_NAMES
+ *
+ * @see #IGNORE_ATTRIBUTE_NAMES
+ */
+ public static final boolean IGNORE_ATTRIBUTE_NAMES_DEFAULT = false;
+
+ /**
+ * Default value for the preference #ATTRIBUTE_NAMES_TO_IGNORE
+ *
+ * @see #ATTRIBUTE_NAMES_TO_IGNORE
+ */
+ public static final String ATTRIBUTE_NAMES_TO_IGNORE_DEFAULT = ""; //$NON-NLS-1$
+
public static final String USE_PROJECT_SETTINGS = "use-project-settings";//$NON-NLS-1$
+ public static final String IGNORE_ATTRIBUTE_NAMES = "ignoreAttrNames";//$NON-NLS-1$
+ public static final String ATTRIBUTE_NAMES_TO_IGNORE = "attrNamesToIgnore";//$NON-NLS-1$
public static final String ATTRIBUTE_UNDEFINED_NAME = "attrUndefName";//$NON-NLS-1$
public static final String ATTRIBUTE_UNDEFINED_VALUE = "attrUndefValue";//$NON-NLS-1$
@@ -252,7 +269,6 @@ public class HTMLCorePreferenceNames {
public static final String ATTRIBUTE_OBSOLETE_NAME = "attrObsoleteName";//$NON-NLS-1$
public static final String ATTRIBUTE_VALUE_EQUALS_MISSING = "attrValueEqualsMissing";//$NON-NLS-1$
-
public static final String ELEM_UNKNOWN_NAME = "elemUnknownName";//$NON-NLS-1$
public static final String ELEM_INVALID_NAME = "elemInvalidName";//$NON-NLS-1$
public static final String ELEM_START_INVALID_CASE = "elemStartInvalidCase";//$NON-NLS-1$
diff --git a/bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/internal/validate/HTMLAttributeValidator.java b/bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/internal/validate/HTMLAttributeValidator.java
index c4d794f..9acadf0 100644
--- a/bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/internal/validate/HTMLAttributeValidator.java
+++ b/bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/internal/validate/HTMLAttributeValidator.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2004, 2013 IBM Corporation and others.
+ * Copyright (c) 2004, 2014 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -7,18 +7,35 @@
*
* Contributors:
* IBM Corporation - initial API and implementation
+ * Red Hat, Inc. - Bug #426939 - [validator] HTML5 attribute validator
+ * marks ng-app AngularJS attributes as undefined
*******************************************************************************/
package org.eclipse.wst.html.core.internal.validate;
import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
import java.util.List;
import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.ProjectScope;
import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.preferences.DefaultScope;
+import org.eclipse.core.runtime.preferences.IPreferencesService;
+import org.eclipse.core.runtime.preferences.IScopeContext;
+import org.eclipse.core.runtime.preferences.InstanceScope;
+import org.eclipse.wst.html.core.internal.HTMLCorePlugin;
import org.eclipse.wst.html.core.internal.document.HTMLDocumentTypeConstants;
+import org.eclipse.wst.html.core.internal.preferences.HTMLCorePreferenceNames;
import org.eclipse.wst.sse.core.internal.provisional.INodeNotifier;
import org.eclipse.wst.sse.core.internal.provisional.IndexedRegion;
import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegion;
@@ -49,6 +66,9 @@ public class HTMLAttributeValidator extends PrimeValidator {
private static final char SINGLE_QUOTE = '\'';
private static final char DOUBLE_QUOTE = '\"';
+ private IPreferencesService fPreferenceService;
+ private static Map fIgnorePatterns = new HashMap(); // A storage for ignore patterns (instances of StringMatcher)
+
// HTML(5) data attributes
private static final String ATTR_NAME_DATA = "data-"; //$NON-NLS-1$
private static final int ATTR_NAME_DATA_LENGTH = ATTR_NAME_DATA.length();
@@ -63,6 +83,7 @@ public class HTMLAttributeValidator extends PrimeValidator {
*/
public HTMLAttributeValidator() {
super();
+ fPreferenceService = Platform.getPreferencesService();
}
/**
@@ -153,11 +174,15 @@ public class HTMLAttributeValidator extends PrimeValidator {
}
if (adec == null) {
- if ((attrName.startsWith(ATTR_NAME_DATA) && attrName.length() > ATTR_NAME_DATA_LENGTH) || (attrName.startsWith(ATTR_NAME_USER_AGENT_FEATURE) && attrName.length() > ATTR_NAME_USER_AGENT_FEATURE_LENGTH)) {
- DocumentTypeAdapter documentTypeAdapter = (DocumentTypeAdapter) ((INodeNotifier) target.getOwnerDocument()).getAdapterFor(DocumentTypeAdapter.class);
- if (documentTypeAdapter != null && documentTypeAdapter.hasFeature(HTMLDocumentTypeConstants.HTML5))
+ if ((attrName.startsWith(ATTR_NAME_DATA) && attrName.length() > ATTR_NAME_DATA_LENGTH) ||
+ (attrName.startsWith(ATTR_NAME_USER_AGENT_FEATURE) && attrName.length() > ATTR_NAME_USER_AGENT_FEATURE_LENGTH)) {
+ if (isHTML5(target))
continue;
- }
+ }
+ // Check for user-defined exclusions for HTML5 attribute names
+ if (!shouldValidateAttributeName(target, attrName))
+ continue;
+
// No attr declaration was found. That is, the attr name is
// undefined.
// but not regard it as undefined name if it includes nested
@@ -333,4 +358,60 @@ public class HTMLAttributeValidator extends PrimeValidator {
return (c == SINGLE_QUOTE) || (c == DOUBLE_QUOTE);
}
// D210422
+
+ private boolean isHTML5(Element target) {
+ DocumentTypeAdapter documentTypeAdapter = (DocumentTypeAdapter) ((INodeNotifier) target.getOwnerDocument()).getAdapterFor(DocumentTypeAdapter.class);
+ return (documentTypeAdapter != null &&
+ documentTypeAdapter.hasFeature(HTMLDocumentTypeConstants.HTML5));
+ }
+
+ private boolean shouldValidateAttributeName(Element target, String attrName) {
+ if (!isHTML5(target)) return true;
+
+ Object adapter = (target instanceof IAdaptable ? ((IAdaptable)target).getAdapter(IResource.class) : null);
+ IProject project = (adapter instanceof IResource ? ((IResource)adapter).getProject() : null);
+
+ Iterator excludedAttributes = getExcludedAttributeNames(project).iterator();
+ while (excludedAttributes.hasNext()) {
+ String excluded = (String)excludedAttributes.next();
+ StringMatcher strMatcher = (StringMatcher)fIgnorePatterns.get(excluded);
+ if (strMatcher == null) {
+ strMatcher = new StringMatcher(excluded);
+ fIgnorePatterns.put(excluded, strMatcher);
+ }
+ if (strMatcher.match(attrName))
+ return false;
+ }
+
+ return true;
+ }
+
+ private Set getExcludedAttributeNames(IProject project) {
+ IScopeContext[] fLookupOrder = new IScopeContext[] {new InstanceScope(), new DefaultScope()};
+ if (project != null) {
+ ProjectScope projectScope = new ProjectScope(project);
+ if(projectScope.getNode(HTMLCorePlugin.getDefault().getBundle().getSymbolicName()).getBoolean(HTMLCorePreferenceNames.USE_PROJECT_SETTINGS, false))
+ fLookupOrder = new IScopeContext[] {projectScope, new InstanceScope(), new DefaultScope()};
+ }
+
+ Set result = new HashSet();
+ if (fPreferenceService.getBoolean(HTMLCorePlugin.getDefault().getBundle().getSymbolicName(),
+ HTMLCorePreferenceNames.IGNORE_ATTRIBUTE_NAMES, HTMLCorePreferenceNames.IGNORE_ATTRIBUTE_NAMES_DEFAULT,
+ fLookupOrder)) {
+ String ignoreList = fPreferenceService.getString(HTMLCorePlugin.getDefault().getBundle().getSymbolicName(),
+ HTMLCorePreferenceNames.ATTRIBUTE_NAMES_TO_IGNORE, HTMLCorePreferenceNames.ATTRIBUTE_NAMES_TO_IGNORE_DEFAULT,
+ fLookupOrder);
+
+ if (ignoreList.trim().isEmpty())
+ return result;
+
+ String[] names = ignoreList.split(","); //$NON-NLS-1$
+ for (int i = 0; names != null && i < names.length; i++) {
+ String name = names[i] == null ? null : names[i].trim();
+ if (name != null && !name.isEmpty())
+ result.add(name.toLowerCase());
+ }
+ }
+ return result;
+ }
}
diff --git a/bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/internal/validate/StringMatcher.java b/bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/internal/validate/StringMatcher.java
new file mode 100644
index 0000000..425ef99
--- /dev/null
+++ b/bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/internal/validate/StringMatcher.java
@@ -0,0 +1,232 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2014 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Red Hat, Inc. - Bug #426939 - [validator] HTML5 attribute validator
+ * marks ng-app AngularJS attributes as undefined
+ *******************************************************************************/
+package org.eclipse.wst.html.core.internal.validate;
+
+import java.util.ArrayList;
+
+/**
+ * A string pattern matcher, supporting "*" and "?" wild cards.
+ *
+ * @since 3.2
+ */
+public class StringMatcher {
+ private static final char SINGLE_WILD_CARD = '\u0000';
+
+ /**
+ * Boundary value beyond which we don't need to search in the text
+ */
+ private int bound = 0;
+
+ private boolean hasLeadingStar;
+
+ private boolean hasTrailingStar;
+
+ private final String pattern;
+
+ private final int patternLength;
+
+ /**
+ * The pattern split into segments separated by *
+ */
+ private String segments[];
+
+ /**
+ * StringMatcher constructor takes in a String object that is a simple
+ * pattern which may contain '*' for 0 and many characters and
+ * '?' for exactly one character.
+ *
+ * Literal '*' and '?' characters must be escaped in the pattern
+ * e.g., "\*" means literal "*", etc.
+ *
+ * Escaping any other character (including the escape character itself),
+ * just results in that character in the pattern.
+ * e.g., "\a" means "a" and "\\" means "\"
+ *
+ * If invoking the StringMatcher with string literals in Java, don't forget
+ * escape characters are represented by "\\".
+ *
+ * @param pattern the pattern to match text against
+ */
+ public StringMatcher(String pattern) {
+ if (pattern == null)
+ throw new IllegalArgumentException();
+ this.pattern = pattern;
+ patternLength = pattern.length();
+ parseWildCards();
+ }
+
+ /**
+ * @param text a simple regular expression that may only contain '?'(s)
+ * @param start the starting index in the text for search, inclusive
+ * @param end the stopping point of search, exclusive
+ * @param p a simple regular expression that may contain '?'
+ * @return the starting index in the text of the pattern , or -1 if not found
+ */
+ private int findPosition(String text, int start, int end, String p) {
+ boolean hasWildCard = p.indexOf(SINGLE_WILD_CARD) >= 0;
+ int plen = p.length();
+ for (int i = start, max = end - plen; i <= max; ++i) {
+ if (hasWildCard) {
+ if (regExpRegionMatches(text, i, p, 0, plen))
+ return i;
+ } else {
+ if (text.regionMatches(true, i, p, 0, plen))
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Given the starting (inclusive) and the ending (exclusive) positions in the
+ * <code>text</code>, determine if the given substring matches with aPattern
+ * @return true if the specified portion of the text matches the pattern
+ * @param text a String object that contains the substring to match
+ */
+ public boolean match(String text) {
+ if (text == null)
+ return false;
+ final int end = text.length();
+ final int segmentCount = segments.length;
+ if (segmentCount == 0 && (hasLeadingStar || hasTrailingStar)) // pattern contains only '*'(s)
+ return true;
+ if (end == 0)
+ return patternLength == 0;
+ if (patternLength == 0)
+ return false;
+ int currentTextPosition = 0;
+ if ((end - bound) < 0)
+ return false;
+ int segmentIndex = 0;
+ String current = segments[segmentIndex];
+
+ /* process first segment */
+ if (!hasLeadingStar) {
+ int currentLength = current.length();
+ if (!regExpRegionMatches(text, 0, current, 0, currentLength))
+ return false;
+ segmentIndex++;
+ currentTextPosition = currentTextPosition + currentLength;
+ }
+ if ((segmentCount == 1) && (!hasLeadingStar) && (!hasTrailingStar)) {
+ // only one segment to match, no wild cards specified
+ return currentTextPosition == end;
+ }
+ /* process middle segments */
+ while (segmentIndex < segmentCount) {
+ current = segments[segmentIndex];
+ int currentMatch = findPosition(text, currentTextPosition, end, current);
+ if (currentMatch < 0)
+ return false;
+ currentTextPosition = currentMatch + current.length();
+ segmentIndex++;
+ }
+
+ /* process final segment */
+ if (!hasTrailingStar && currentTextPosition != end) {
+ int currentLength = current.length();
+ return regExpRegionMatches(text, end - currentLength, current, 0, currentLength);
+ }
+ return segmentIndex == segmentCount;
+ }
+
+ /**
+ * Parses the pattern into segments separated by wildcard '*' characters.
+ */
+ private void parseWildCards() {
+ if (pattern.startsWith("*"))//$NON-NLS-1$
+ hasLeadingStar = true;
+ if (pattern.endsWith("*")) {//$NON-NLS-1$
+ /* make sure it's not an escaped wildcard */
+ if (patternLength > 1 && pattern.charAt(patternLength - 2) != '\\') {
+ hasTrailingStar = true;
+ }
+ }
+
+ ArrayList temp = new ArrayList();
+
+ int pos = 0;
+ StringBuffer buf = new StringBuffer();
+ while (pos < patternLength) {
+ char c = pattern.charAt(pos++);
+ switch (c) {
+ case '\\' :
+ if (pos >= patternLength) {
+ buf.append(c);
+ } else {
+ char next = pattern.charAt(pos++);
+ /* if it's an escape sequence */
+ if (next == '*' || next == '?' || next == '\\') {
+ buf.append(next);
+ } else {
+ /* not an escape sequence, just insert literally */
+ buf.append(c);
+ buf.append(next);
+ }
+ }
+ break;
+ case '*' :
+ if (buf.length() > 0) {
+ /* new segment */
+ temp.add(buf.toString());
+ bound += buf.length();
+ buf.setLength(0);
+ }
+ break;
+ case '?' :
+ /* append special character representing single match wildcard */
+ buf.append(SINGLE_WILD_CARD);
+ break;
+ default :
+ buf.append(c);
+ }
+ }
+
+ /* add last buffer to segment list */
+ if (buf.length() > 0) {
+ temp.add(buf.toString());
+ bound += buf.length();
+ }
+ segments = (String[])temp.toArray(new String[temp.size()]);
+ }
+
+ /**
+ *
+ * @return boolean
+ * @param text a String to match
+ * @param tStart the starting index of match, inclusive
+ * @param p a simple regular expression that may contain '?'
+ * @param pStart The start position in the pattern
+ * @param plen The length of the pattern
+ */
+ private boolean regExpRegionMatches(String text, int tStart, String p, int pStart, int plen) {
+ while (plen-- > 0) {
+ char tchar = text.charAt(tStart++);
+ char pchar = p.charAt(pStart++);
+
+ // process wild cards, skipping single wild cards
+ if (pchar == SINGLE_WILD_CARD)
+ continue;
+ if (pchar == tchar)
+ continue;
+ if (Character.toUpperCase(tchar) == Character.toUpperCase(pchar))
+ continue;
+ // comparing after converting to upper case doesn't handle all cases;
+ // also compare after converting to lower case
+ if (Character.toLowerCase(tchar) == Character.toLowerCase(pchar))
+ continue;
+ return false;
+ }
+ return true;
+ }
+} \ No newline at end of file