Bug 415980 - HTML5 attribute validator marks ng-app AngularJS attributes as undefined
A new preference is created for the pattern of attribute names to be ignored by HTMLAttributeValidator
Web->HTML Files->Validation preference page is modified due to allow the ignored attribute names to be shown/edited by users.
New Quick-fix is added due to allow the users to add attribute names and attribute name patterns to the HTML Attribute Validator
ignore list.
Signed-off-by: Victor V Rubezhny <vrubezhny@exadel.com>
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..f3a4b14 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, 2013 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
@@ -72,6 +72,10 @@
* @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..2b9e70e 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, 2013 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
@@ -238,7 +238,23 @@
*/
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 +268,6 @@
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..bf68297 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
@@ -11,14 +11,29 @@
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 +64,9 @@
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 +81,7 @@
*/
public HTMLAttributeValidator() {
super();
+ fPreferenceService = Platform.getPreferencesService();
}
/**
@@ -153,11 +172,15 @@
}
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 +356,60 @@
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..6871163
--- /dev/null
+++ b/bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/internal/validate/StringMatcher.java
@@ -0,0 +1,230 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2013 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
+ *******************************************************************************/
+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
diff --git a/bundles/org.eclipse.wst.html.ui/icons/full/etool16/donotvalidate.png b/bundles/org.eclipse.wst.html.ui/icons/full/etool16/donotvalidate.png
new file mode 100644
index 0000000..ecb20f5
--- /dev/null
+++ b/bundles/org.eclipse.wst.html.ui/icons/full/etool16/donotvalidate.png
Binary files differ
diff --git a/bundles/org.eclipse.wst.html.ui/plugin.xml b/bundles/org.eclipse.wst.html.ui/plugin.xml
index 1461078..8bf535d 100644
--- a/bundles/org.eclipse.wst.html.ui/plugin.xml
+++ b/bundles/org.eclipse.wst.html.ui/plugin.xml
@@ -50,6 +50,10 @@
class="org.eclipse.wst.xml.ui.internal.correction.XMLQuickAssistProcessor"
target="org.eclipse.wst.html.HTML_DEFAULT" />
<provisionalConfiguration
+ type="org.eclipse.jface.text.quickassist.IQuickAssistProcessor"
+ class="org.eclipse.wst.html.ui.internal.text.correction.HTMLAttributeValidationQuickFixProcessor"
+ target="org.eclipse.wst.html.HTML_DEFAULT" />
+ <provisionalConfiguration
type="autoeditstrategy"
class="org.eclipse.wst.html.ui.internal.autoedit.StructuredAutoEditStrategyHTML"
target="org.eclipse.wst.html.HTML_DEFAULT, org.eclipse.wst.html.HTML_DECLARATION" />
diff --git a/bundles/org.eclipse.wst.html.ui/src/org/eclipse/wst/html/ui/internal/HTMLUIMessages.java b/bundles/org.eclipse.wst.html.ui/src/org/eclipse/wst/html/ui/internal/HTMLUIMessages.java
index e1f4b83..1538506 100644
--- a/bundles/org.eclipse.wst.html.ui/src/org/eclipse/wst/html/ui/internal/HTMLUIMessages.java
+++ b/bundles/org.eclipse.wst.html.ui/src/org/eclipse/wst/html/ui/internal/HTMLUIMessages.java
@@ -1,5 +1,5 @@
/**********************************************************************
- * Copyright (c) 2005, 2011 IBM Corporation and others. All rights reserved. This
+ * Copyright (c) 2005, 2013 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
@@ -209,4 +209,13 @@
// Hyperlinks
public static String Hyperlink_line;
public static String Open;
+
+ // Ignore Attribute Name Patterns
+ public static String IgnoreAttributeNames;
+ public static String IgnoreAttributeNamesPattern;
+ public static String BadIgnoreAttributeNamesPattern;
+ public static String DoNotValidateAttribute;
+ public static String DoNotValidateAllAttributes;
+ public static String DoNotValidateAttributeAddInfo;
+ public static String DoNotValidateAllAttributesAddInfo;
}
diff --git a/bundles/org.eclipse.wst.html.ui/src/org/eclipse/wst/html/ui/internal/HTMLUIPluginResources.properties b/bundles/org.eclipse.wst.html.ui/src/org/eclipse/wst/html/ui/internal/HTMLUIPluginResources.properties
index abf07c6..b1e3b78 100644
--- a/bundles/org.eclipse.wst.html.ui/src/org/eclipse/wst/html/ui/internal/HTMLUIPluginResources.properties
+++ b/bundles/org.eclipse.wst.html.ui/src/org/eclipse/wst/html/ui/internal/HTMLUIPluginResources.properties
@@ -1,5 +1,5 @@
###############################################################################
-# Copyright (c) 2004, 2012 IBM Corporation and others.
+# Copyright (c) 2004, 2013 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
@@ -181,3 +181,11 @@
Hyperlink_line={0}={1} : line {2}
Open=Open ''{0}''
+
+IgnoreAttributeNames=Ignore specified attribute names in validation
+IgnoreAttributeNamesPattern=Specify comma-separated patterns for attribute names to be ignored:
+BadIgnoreAttributeNamesPattern=''{0}'' is not a valid attribute names pattern
+DoNotValidateAttribute=Ignore ''{0}'' attribute in validation
+DoNotValidateAllAttributes=Ignore all ''{0}'' attributes in validation
+DoNotValidateAttributeAddInfo=Adds the name of attribute to the list of ignored attributes for HTML Attribute Validator<br>You may edit the list of ignored attributes at <b>Web→HTML Files→Validation</b> Preference Page
+DoNotValidateAllAttributesAddInfo=Adds the pattern based on the attribute name to the list of ignored attributes for HTML Attribute Validator<br>You may edit the list of ignored attributes at <b>Web→HTML Files→Validation</b> Preference Page
\ No newline at end of file
diff --git a/bundles/org.eclipse.wst.html.ui/src/org/eclipse/wst/html/ui/internal/editor/HTMLEditorPluginImages.java b/bundles/org.eclipse.wst.html.ui/src/org/eclipse/wst/html/ui/internal/editor/HTMLEditorPluginImages.java
index 1fa0f24..be2c050 100644
--- a/bundles/org.eclipse.wst.html.ui/src/org/eclipse/wst/html/ui/internal/editor/HTMLEditorPluginImages.java
+++ b/bundles/org.eclipse.wst.html.ui/src/org/eclipse/wst/html/ui/internal/editor/HTMLEditorPluginImages.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2004, 2006 IBM Corporation and others.
+ * Copyright (c) 2004, 2013 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
@@ -29,4 +29,5 @@
public static final String IMG_OBJ_TAG_TITLE = "icons/full/obj16/tag-title.gif"; //$NON-NLS-1$
public static final String IMG_OBJ_TAG = "icons/full/obj16/tag.gif"; //$NON-NLS-1$
public static final String IMG_WIZBAN_NEWHTMLFILE = "icons/full/wizban/newhfile_wiz.png"; //$NON-NLS-1$
-}
+ public static final String IMG_DTOOL_DO_NOT_VALIDATE = "icons/full/etool16/donotvalidate.png"; //$NON-NLS-1$
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.wst.html.ui/src/org/eclipse/wst/html/ui/internal/preferences/ui/HTMLValidationPreferencePage.java b/bundles/org.eclipse.wst.html.ui/src/org/eclipse/wst/html/ui/internal/preferences/ui/HTMLValidationPreferencePage.java
index e3db43c..3e14f55 100644
--- a/bundles/org.eclipse.wst.html.ui/src/org/eclipse/wst/html/ui/internal/preferences/ui/HTMLValidationPreferencePage.java
+++ b/bundles/org.eclipse.wst.html.ui/src/org/eclipse/wst/html/ui/internal/preferences/ui/HTMLValidationPreferencePage.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2008, 2012 IBM Corporation and others.
+ * Copyright (c) 2008, 2013 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
@@ -10,35 +10,152 @@
*******************************************************************************/
package org.eclipse.wst.html.ui.internal.preferences.ui;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.preferences.DefaultScope;
+import org.eclipse.core.runtime.preferences.IEclipsePreferences;
+import org.eclipse.core.runtime.preferences.IPreferencesService;
+import org.eclipse.core.runtime.preferences.IScopeContext;
import org.eclipse.jface.dialogs.IDialogSettings;
import org.eclipse.jface.layout.PixelConverter;
+import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.swt.widgets.Widget;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.forms.widgets.ExpandableComposite;
import org.eclipse.wst.html.core.internal.HTMLCorePlugin;
import org.eclipse.wst.html.core.internal.preferences.HTMLCorePreferenceNames;
import org.eclipse.wst.html.ui.internal.HTMLUIMessages;
import org.eclipse.wst.html.ui.internal.HTMLUIPlugin;
+import org.eclipse.wst.html.ui.internal.Logger;
import org.eclipse.wst.sse.core.internal.validate.ValidationMessage;
import org.eclipse.wst.sse.ui.internal.preferences.ui.AbstractValidationSettingsPage;
import org.eclipse.wst.sse.ui.internal.preferences.ui.ScrolledPageContent;
+import org.osgi.service.prefs.BackingStoreException;
public class HTMLValidationPreferencePage extends AbstractValidationSettingsPage {
+ public static final String PROPERTY_PAGE_ID = "org.eclipse.wst.html.ui.propertyPage.project.validation";
+
+ public static final String PREFERENCE_PAGE_ID = "org.eclipse.wst.html.ui.preferences.validation";
+
private static final int[] SEVERITIES = {ValidationMessage.ERROR, ValidationMessage.WARNING, ValidationMessage.IGNORE};
private static final String SETTINGS_SECTION_NAME = "HTMLValidationSeverities";//$NON-NLS-1$
+
+ private class BooleanData {
+ private String fKey;
+ private boolean fValue;
+ boolean originalValue = false;
+
+ public BooleanData(String key) {
+ fKey = key;
+ }
+
+ public String getKey() {
+ return fKey;
+ }
+
+ /**
+ * Sets enablement for the attribute names ignorance
+ *
+ * @param severity the severity level
+ */
+ public void setValue(boolean value) {
+ fValue = value;
+ }
+
+ /**
+ * Returns the value for the attribute names ignorance
+ *
+ * @return
+ */
+ public boolean getValue() {
+ return fValue;
+ }
+
+ boolean isChanged() {
+ return (originalValue != fValue);
+ }
+ }
+
+ private class TextData {
+ private String fKey;
+ private String fValue;
+ String originalValue = ""; //$NON-NLS-1$
+
+ public TextData(String key) {
+ fKey = key;
+ }
+
+ public String getKey() {
+ return fKey;
+ }
+
+ /**
+ * Sets the ignored attribute names pattern
+ *
+ * @param severity the severity level
+ */
+ public void setValue(String value) {
+ fValue = value;
+ }
+
+ /**
+ * Returns non-null value for the ignored attribute names pattern
+ *
+ * @return
+ */
+ public String getValue() {
+ return fValue != null ? fValue : ""; //$NON-NLS-1$
+ }
+
+ boolean isChanged() {
+ return !originalValue.equalsIgnoreCase(fValue);
+ }
+ }
public HTMLValidationPreferencePage() {
super();
+ fPreferencesService = Platform.getPreferencesService();
}
private PixelConverter fPixelConverter;
+ private Button fIgnoreAttributeNames;
+ private Label fIgnoredAttributeNamesLabel;
+ private Text fIgnoredAttributeNames;
+ private IPreferencesService fPreferencesService = null;
+
+ private boolean fUseOriginOverrides = false;
+ private boolean fIgnoreAttributeNamesOriginOverride = HTMLCorePreferenceNames.IGNORE_ATTRIBUTE_NAMES_DEFAULT;
+ private String fIgnoredAttributeNamesOriginOverride = HTMLCorePreferenceNames.ATTRIBUTE_NAMES_TO_IGNORE_DEFAULT;
+
+ public void overrideOriginValues(boolean enableIgnore, String attributeNames) {
+ fIgnoreAttributeNamesOriginOverride = enableIgnore;
+ fIgnoredAttributeNamesOriginOverride = attributeNames;
+ fUseOriginOverrides = true;
+
+ if (fIgnoreAttributeNames != null) {
+ BooleanData data = (BooleanData)fIgnoreAttributeNames.getData();
+ if (data != null)
+ data.originalValue = fIgnoreAttributeNamesOriginOverride;
+ }
+ if (fIgnoredAttributeNames != null) {
+ TextData data = (TextData)fIgnoredAttributeNames.getData();
+ if (data != null)
+ data.originalValue = fIgnoredAttributeNamesOriginOverride;
+ }
+ }
protected Control createCommonContents(Composite parent) {
final Composite page = new Composite(parent, SWT.NULL);
@@ -71,6 +188,59 @@
layout.marginWidth= 0;
composite.setLayout(layout);
+ // Ignored Attribute Names Pattern
+ BooleanData ignoreData = new BooleanData(HTMLCorePreferenceNames.IGNORE_ATTRIBUTE_NAMES);
+ fIgnoreAttributeNames = new Button(composite, SWT.CHECK);
+ fIgnoreAttributeNames.setData(ignoreData);
+ fIgnoreAttributeNames.setFont(page.getFont());
+ fIgnoreAttributeNames.setText(HTMLUIMessages.IgnoreAttributeNames);
+ fIgnoreAttributeNames.setEnabled(true);
+
+ boolean ignoreAttributeNamesIsSelected = fPreferencesService.getBoolean(getPreferenceNodeQualifier(),
+ ignoreData.getKey(), HTMLCorePreferenceNames.IGNORE_ATTRIBUTE_NAMES_DEFAULT, createPreferenceScopes());
+ ignoreData.setValue(ignoreAttributeNamesIsSelected);
+ ignoreData.originalValue = fUseOriginOverrides ? fIgnoreAttributeNamesOriginOverride : ignoreAttributeNamesIsSelected;
+
+ fIgnoreAttributeNames.setSelection(ignoreData.getValue());
+ fIgnoreAttributeNames.addSelectionListener(new SelectionListener() {
+ public void widgetDefaultSelected(SelectionEvent e) {
+ controlChanged(e.widget);
+ }
+ public void widgetSelected(SelectionEvent e) {
+ controlChanged(e.widget);
+ }
+ });
+ fIgnoreAttributeNames.setLayoutData(new GridData(GridData.FILL, GridData.CENTER, true, false, 3, 1));
+
+ fIgnoredAttributeNamesLabel = new Label(composite, SWT.LEFT | SWT.WRAP);
+ fIgnoredAttributeNamesLabel.setFont(composite.getFont());
+ fIgnoredAttributeNamesLabel.setEnabled(ignoreData.getValue());
+ fIgnoredAttributeNamesLabel.setText(HTMLUIMessages.IgnoreAttributeNamesPattern);
+ fIgnoredAttributeNamesLabel.setLayoutData(new GridData(SWT.FILL, SWT.END, true, false, 3, 1));
+ setHorizontalIndent(fIgnoredAttributeNamesLabel, 20);
+
+ TextData data = new TextData(HTMLCorePreferenceNames.ATTRIBUTE_NAMES_TO_IGNORE);
+ fIgnoredAttributeNames = new Text(composite, SWT.SINGLE | SWT.BORDER);
+ fIgnoredAttributeNames.setData(data);
+ fIgnoredAttributeNames.setTextLimit(500);
+ fIgnoredAttributeNames.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false, 3, 1));
+ setHorizontalIndent(fIgnoredAttributeNames, 20);
+ setWidthHint(fIgnoredAttributeNames, convertWidthInCharsToPixels(65));
+ String ignoredAttributeNames = fPreferencesService.getString(getPreferenceNodeQualifier(), data.getKey(), HTMLCorePreferenceNames.ATTRIBUTE_NAMES_TO_IGNORE_DEFAULT, createPreferenceScopes());
+ data.setValue(ignoredAttributeNames);
+ data.originalValue = fUseOriginOverrides ? fIgnoredAttributeNamesOriginOverride : ignoredAttributeNames;
+ fIgnoredAttributeNames.setText(data.getValue());
+
+ fIgnoredAttributeNames.addModifyListener(new ModifyListener() {
+
+ public void modifyText(ModifyEvent e) {
+ if (verifyIgnoredAttributeNames()) {
+ controlChanged(e.widget);
+ }
+ }
+ });
+ controlChanged(fIgnoreAttributeNames);
+
Label description = new Label(composite, SWT.NONE);
description.setText(HTMLUIMessages.Validation_description);
description.setFont(page.getFont());
@@ -285,16 +455,129 @@
return spContent;
}
+
+ private void setHorizontalIndent(Control control, int indent) {
+ Object ld= control.getLayoutData();
+ if (ld instanceof GridData) {
+ ((GridData) ld).horizontalIndent= indent;
+ }
+ }
+
+ private void setWidthHint(Control control, int widthHint) {
+ Object ld= control.getLayoutData();
+ if (ld instanceof GridData) {
+ ((GridData)ld).widthHint= widthHint;
+ }
+ }
+
+ private boolean verifyIgnoredAttributeNames() {
+ final String text = fIgnoredAttributeNames.getText().trim();
+ if (text.length() == 0)
+ return true;
+
+ boolean valid = true;
+ for (int i = 0; valid && i < text.length(); i++) {
+ if (!Character.isJavaIdentifierPart(text.charAt(i)) &&
+ '-' != text.charAt(i) && '_' != text.charAt(i) &&
+ '*' != text.charAt(i) && '?' != text.charAt(i) &&
+ ',' != text.charAt(i))
+ valid = false;
+ }
+
+ if (!valid) {
+ setErrorMessage(NLS.bind(HTMLUIMessages.BadIgnoreAttributeNamesPattern, text));
+ setValid(false);
+ }
+ else {
+ setErrorMessage(null);
+ setValid(true);
+ }
+ return valid;
+ }
+
+ protected void controlChanged(Widget widget) {
+ if (widget instanceof Text) {
+ TextData data= (TextData) widget.getData();
+ data.setValue(((Text)widget).getText());
+ } else if (widget instanceof Button) {
+ BooleanData data = (BooleanData) widget.getData();
+ if (data != null) {
+ data.setValue(((Button)widget).getSelection());
+ fIgnoredAttributeNamesLabel.setEnabled(data.getValue());
+ fIgnoredAttributeNames.setEnabled(data.getValue());
+ if (data.getValue()) {
+ fIgnoredAttributeNames.setFocus();
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns true in case of the Attribute Names to ignore preferences is changed
+ * causing the full validation to be requested.
+ */
+ protected boolean shouldRevalidateOnSettingsChange() {
+ TextData data = (TextData)fIgnoredAttributeNames.getData();
+ if (data.isChanged())
+ return true;
+
+ BooleanData ignoreData = (BooleanData)fIgnoreAttributeNames.getData();
+ if (ignoreData.isChanged())
+ return true;
+
+ return super.shouldRevalidateOnSettingsChange();
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.wst.sse.ui.internal.preferences.ui.AbstractSettingsPage#storeValues()
+ */
+ protected void storeValues() {
+ IScopeContext[] contexts = createPreferenceScopes();
+
+ BooleanData ignoreData = (BooleanData)fIgnoreAttributeNames.getData();
+ contexts[0].getNode(getPreferenceNodeQualifier()).putBoolean(ignoreData.getKey(), ignoreData.getValue());
+
+ TextData data = (TextData)fIgnoredAttributeNames.getData();
+ contexts[0].getNode(getPreferenceNodeQualifier()).put(data.getKey(), data.getValue());
+
+
+ for(int i = 0; i < contexts.length; i++) {
+ try {
+ contexts[i].getNode(getPreferenceNodeQualifier()).flush();
+ } catch (BackingStoreException e) {
+ Logger.logException(e);
+ }
+ }
+ super.storeValues();
+ }
/*
* (non-Javadoc)
* @see org.eclipse.jface.preference.PreferencePage#performDefaults()
*/
protected void performDefaults() {
+ resetIgnoreAttributeNamesPattern();
resetSeverities();
super.performDefaults();
}
+ protected void resetIgnoreAttributeNamesPattern() {
+ IEclipsePreferences defaultContext = new DefaultScope().getNode(getPreferenceNodeQualifier());
+ BooleanData ignoreData = (BooleanData)fIgnoreAttributeNames.getData();
+ boolean ignoreAttributeNames = defaultContext.getBoolean(ignoreData.getKey(), HTMLCorePreferenceNames.IGNORE_ATTRIBUTE_NAMES_DEFAULT);
+ ignoreData.setValue(ignoreAttributeNames);
+ fIgnoreAttributeNames.setSelection(ignoreData.getValue());
+
+ TextData data = (TextData)fIgnoredAttributeNames.getData();
+ String ignoredAttributeNames = defaultContext.get(data.getKey(), HTMLCorePreferenceNames.ATTRIBUTE_NAMES_TO_IGNORE_DEFAULT);
+ data.setValue(ignoredAttributeNames);
+ fIgnoredAttributeNames.setText(data.getValue());
+
+ controlChanged(fIgnoreAttributeNames);
+ }
+
+
protected IDialogSettings getDialogSettings() {
return HTMLUIPlugin.getDefault().getDialogSettings();
}
@@ -317,7 +600,7 @@
}
protected String getPreferencePageID() {
- return "org.eclipse.wst.html.ui.preferences.validation";//$NON-NLS-1$
+ return PREFERENCE_PAGE_ID;
}
protected String getProjectSettingsKey() {
@@ -325,7 +608,7 @@
}
protected String getPropertyPageID() {
- return "org.eclipse.wst.html.ui.propertyPage.project.validation";//$NON-NLS-1$
+ return PROPERTY_PAGE_ID;
}
public void init(IWorkbench workbench) {
diff --git a/bundles/org.eclipse.wst.html.ui/src/org/eclipse/wst/html/ui/internal/text/correction/HTMLAttributeValidationQuickFixProcessor.java b/bundles/org.eclipse.wst.html.ui/src/org/eclipse/wst/html/ui/internal/text/correction/HTMLAttributeValidationQuickFixProcessor.java
new file mode 100644
index 0000000..5e3f7cd
--- /dev/null
+++ b/bundles/org.eclipse.wst.html.ui/src/org/eclipse/wst/html/ui/internal/text/correction/HTMLAttributeValidationQuickFixProcessor.java
@@ -0,0 +1,228 @@
+/*******************************************************************************
+ * Copyright (c) 2013 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
+ *******************************************************************************/
+package org.eclipse.wst.html.ui.internal.text.correction;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+import org.eclipse.core.resources.IMarker;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.ProjectScope;
+import org.eclipse.core.runtime.IAdaptable;
+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.jface.text.contentassist.ICompletionProposal;
+import org.eclipse.jface.text.quickassist.IQuickAssistInvocationContext;
+import org.eclipse.jface.text.quickassist.IQuickAssistProcessor;
+import org.eclipse.jface.text.source.Annotation;
+import org.eclipse.jface.text.source.IAnnotationModel;
+import org.eclipse.jface.text.source.IAnnotationModelExtension2;
+import org.eclipse.jface.text.source.ISourceViewer;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.ui.texteditor.MarkerAnnotation;
+import org.eclipse.wst.html.core.internal.HTMLCoreMessages;
+import org.eclipse.wst.html.core.internal.HTMLCorePlugin;
+import org.eclipse.wst.html.core.internal.preferences.HTMLCorePreferenceNames;
+import org.eclipse.wst.html.core.internal.validate.StringMatcher;
+import org.eclipse.wst.html.ui.internal.HTMLUIMessages;
+import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegion;
+import org.eclipse.wst.sse.ui.internal.contentassist.ContentAssistUtils;
+import org.eclipse.wst.sse.ui.internal.reconcile.TemporaryAnnotation;
+import org.eclipse.wst.xml.core.internal.provisional.document.IDOMAttr;
+import org.eclipse.wst.xml.core.internal.provisional.document.IDOMNode;
+import org.w3c.dom.NamedNodeMap;
+
+public class HTMLAttributeValidationQuickFixProcessor implements IQuickAssistProcessor {
+
+ private StringMatcher UNDEFINED_ATTRIBUTE_NAME_MATCHER;
+ private IPreferencesService fPreferenceService;
+
+ public HTMLAttributeValidationQuickFixProcessor() {
+ String templ = HTMLCoreMessages.Undefined_attribute_name___ERROR_;
+ templ = templ.replaceAll("\\{[0-9]*\\}", "\\*"); //$NON-NLS-1$
+ UNDEFINED_ATTRIBUTE_NAME_MATCHER = new StringMatcher(templ);
+ fPreferenceService = Platform.getPreferencesService();
+ }
+
+ /*
+ * @see org.eclipse.jface.text.quickassist.IQuickAssistProcessor#getErrorMessage()
+ */
+ public String getErrorMessage() {
+ return null;
+ }
+
+ /*
+ * @see org.eclipse.jface.text.quickassist.IQuickAssistProcessor#canFix(org.eclipse.jface.text.source.Annotation)
+ */
+ public boolean canFix(Annotation annotation) {
+ boolean result = false;
+
+ String text = null;
+ if (annotation instanceof TemporaryAnnotation) {
+ TemporaryAnnotation tempAnnotation = (TemporaryAnnotation) annotation;
+ int problemID = tempAnnotation.getProblemID();
+ text = tempAnnotation.getText();
+
+ if (problemID == 0 && text != null)
+ result = true;
+ } else if (annotation instanceof MarkerAnnotation) {
+ MarkerAnnotation markerAnnotation = (MarkerAnnotation) annotation;
+ text = markerAnnotation.getText();
+ IMarker marker = markerAnnotation.getMarker();
+ IResource resource = marker == null ? null : marker.getResource();
+ if (resource != null && resource.exists() && resource.isAccessible() && text != null) {
+ result = true;
+ }
+ }
+
+ result = (result && UNDEFINED_ATTRIBUTE_NAME_MATCHER.match(text));
+
+ return result;
+ }
+
+ /*
+ * @see org.eclipse.jface.text.quickassist.IQuickAssistProcessor#canAssist(org.eclipse.jface.text.quickassist.IQuickAssistInvocationContext)
+ */
+ public boolean canAssist(IQuickAssistInvocationContext invocationContext) {
+ return true;
+ }
+
+ /*
+ * @see org.eclipse.jface.text.quickassist.IQuickAssistProcessor#computeQuickAssistProposals(org.eclipse.jface.text.quickassist.IQuickAssistInvocationContext)
+ */
+ public ICompletionProposal[] computeQuickAssistProposals(
+ IQuickAssistInvocationContext invocationContext) {
+ ISourceViewer viewer = invocationContext.getSourceViewer();
+ int documentOffset = invocationContext.getOffset();
+ int length = viewer != null ? viewer.getSelectedRange().y : 0;
+
+ IAnnotationModel model = viewer.getAnnotationModel();
+ if (model == null)
+ return null;
+
+ List proposals = new ArrayList();
+ if (model instanceof IAnnotationModelExtension2) {
+ Iterator iter = ((IAnnotationModelExtension2) model).getAnnotationIterator(documentOffset, length, true, true);
+ while (iter.hasNext()) {
+ Annotation anno = (Annotation) iter.next();
+ if (canFix(anno)) {
+ int offset = -1;
+
+ if (anno instanceof TemporaryAnnotation) {
+ offset = ((TemporaryAnnotation)anno).getPosition().getOffset();
+ } else if (anno instanceof MarkerAnnotation) {
+ offset = ((MarkerAnnotation)anno).getMarker().getAttribute(IMarker.CHAR_START, -1);
+ }
+ if (offset == -1)
+ continue;
+
+ IDOMNode node = (IDOMNode) ContentAssistUtils.getNodeAt(viewer, offset);
+
+ Object adapter = (node instanceof IAdaptable ? ((IAdaptable)node).getAdapter(IResource.class) : null);
+ IProject project = (adapter instanceof IResource ? ((IResource)adapter).getProject() : null);
+
+ IScopeContext[] fLookupOrder = new IScopeContext[] {new InstanceScope(), new DefaultScope()};
+ if (project != null) {
+ ProjectScope projectScope = new ProjectScope(project);
+ if(projectScope.getNode(getPreferenceNodeQualifier())
+ .getBoolean(getProjectSettingsKey(), false))
+ fLookupOrder = new IScopeContext[] {projectScope, new InstanceScope(), new DefaultScope()};
+ }
+
+ String ignoreList = fPreferenceService.getString(
+ getPreferenceNodeQualifier(), HTMLCorePreferenceNames.ATTRIBUTE_NAMES_TO_IGNORE,
+ HTMLCorePreferenceNames.ATTRIBUTE_NAMES_TO_IGNORE_DEFAULT, fLookupOrder);
+
+ Set result = new HashSet();
+ if (!ignoreList.trim().isEmpty()) {
+ 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());
+ }
+ }
+
+ String name = getAttributeName(node, offset);
+ if (name == null) continue;
+
+ if (!result.contains(name.toLowerCase())) {
+ IgnoreAttributeNameCompletionProposal p = new IgnoreAttributeNameCompletionProposal(
+ name, offset, NLS.bind(HTMLUIMessages.DoNotValidateAttribute, name),
+ HTMLUIMessages.DoNotValidateAttributeAddInfo, node);
+ if (!proposals.contains(p))
+ proposals.add(p);
+ }
+
+ int dashIndex = name.indexOf('-');
+ while (dashIndex != -1) {
+ StringBuffer namePattern = new StringBuffer(name.substring(0, dashIndex + 1)).append('*');
+
+ // Do not continue creating proposals for the rest of patterns if
+ // a more common pattern is already created
+ if (result.contains(namePattern.toString().toLowerCase()))
+ break;
+
+ if (!result.contains(namePattern.toString().toLowerCase())) {
+ IgnoreAttributeNameCompletionProposal p = new IgnoreAttributeNameCompletionProposal(
+ namePattern.toString(), offset, NLS.bind(HTMLUIMessages.DoNotValidateAllAttributes, namePattern.toString()),
+ HTMLUIMessages.DoNotValidateAllAttributesAddInfo, node);
+ if (!proposals.contains(p))
+ proposals.add(p);
+ }
+ dashIndex = name.indexOf('-', dashIndex + 1);
+ }
+ }
+ }
+ }
+
+ if (proposals.isEmpty())
+ return null;
+
+ return (ICompletionProposal[]) proposals.toArray(new ICompletionProposal[proposals.size()]);
+
+ }
+
+ private String getAttributeName(IDOMNode node, int offset) {
+ IStructuredDocumentRegion startStructuredDocumentRegion = node == null ? null : node.getStartStructuredDocumentRegion();
+
+ // Only a tag start can have attributes
+ if ((startStructuredDocumentRegion != null) && startStructuredDocumentRegion.containsOffset(offset)) {
+ NamedNodeMap attributes = node.getAttributes();
+ if (attributes == null) return null;
+
+ for (int i = 0; i < attributes.getLength(); i++) {
+ IDOMAttr attr = (IDOMAttr)attributes.item(i);
+
+ if (attr.getNameRegionStartOffset() <= offset && attr.getNameRegionEndOffset() >= offset) {
+ return attr.getName();
+ }
+ }
+ }
+
+ return null;
+ }
+
+ private String getPreferenceNodeQualifier() {
+ return HTMLCorePlugin.getDefault().getBundle().getSymbolicName();
+ }
+
+ private String getProjectSettingsKey() {
+ return HTMLCorePreferenceNames.USE_PROJECT_SETTINGS;
+ }
+}
diff --git a/bundles/org.eclipse.wst.html.ui/src/org/eclipse/wst/html/ui/internal/text/correction/IgnoreAttributeNameCompletionProposal.java b/bundles/org.eclipse.wst.html.ui/src/org/eclipse/wst/html/ui/internal/text/correction/IgnoreAttributeNameCompletionProposal.java
new file mode 100644
index 0000000..14c9f80
--- /dev/null
+++ b/bundles/org.eclipse.wst.html.ui/src/org/eclipse/wst/html/ui/internal/text/correction/IgnoreAttributeNameCompletionProposal.java
@@ -0,0 +1,187 @@
+/*******************************************************************************
+ * Copyright (c) 2013 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
+ *******************************************************************************/
+package org.eclipse.wst.html.ui.internal.text.correction;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.ProjectScope;
+import org.eclipse.core.runtime.IAdaptable;
+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.jface.preference.PreferenceDialog;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.contentassist.ICompletionProposal;
+import org.eclipse.jface.text.contentassist.IContextInformation;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.dialogs.PreferencesUtil;
+import org.eclipse.wst.html.core.internal.HTMLCorePlugin;
+import org.eclipse.wst.html.core.internal.preferences.HTMLCorePreferenceNames;
+import org.eclipse.wst.html.ui.internal.HTMLUIPlugin;
+import org.eclipse.wst.html.ui.internal.Logger;
+import org.eclipse.wst.html.ui.internal.editor.HTMLEditorPluginImageHelper;
+import org.eclipse.wst.html.ui.internal.editor.HTMLEditorPluginImages;
+import org.eclipse.wst.html.ui.internal.preferences.ui.HTMLValidationPreferencePage;
+import org.osgi.service.prefs.BackingStoreException;
+import org.w3c.dom.Node;
+
+public class IgnoreAttributeNameCompletionProposal implements ICompletionProposal {
+ /** The string to be added to the Ignored HTML Attributes list. */
+ private String fPattern;
+ /** The target node */
+ private Node fTarget;
+ /** The string to be displayed in the completion proposal popup. */
+ private String fDisplayString;
+ /** The replacement offset. */
+ private int fReplacementOffset;
+ /** The context information of this proposal. */
+ private IContextInformation fContextInformation;
+ /** The additional info of this proposal. */
+ private String fAdditionalProposalInfo;
+
+ private IPreferencesService fPreferenceService;
+
+ public IgnoreAttributeNameCompletionProposal(String pattern, int offset, String displayString, String additionalProposalInfo, Node target) {
+ fReplacementOffset= offset;
+ fPattern = pattern;
+ fDisplayString= displayString;
+ fAdditionalProposalInfo= additionalProposalInfo;
+ fTarget = target;
+ fPreferenceService = Platform.getPreferencesService();
+ }
+
+ /*
+ * @see ICompletionProposal#apply(IDocument)
+ */
+ public void apply(IDocument document) {
+ Object adapter = (fTarget instanceof IAdaptable ? ((IAdaptable)fTarget).getAdapter(IResource.class) : null);
+ IProject project = (adapter instanceof IResource ? ((IResource)adapter).getProject() : null);
+
+ IScopeContext[] fLookupOrder = new IScopeContext[] {new InstanceScope(), new DefaultScope()};
+ boolean hasProjectSettings = false;
+ if (project != null) {
+ ProjectScope projectScope = new ProjectScope(project);
+ if(projectScope.getNode(getPreferenceNodeQualifier())
+ .getBoolean(getProjectSettingsKey(), false)) {
+ hasProjectSettings = true;
+ fLookupOrder = new IScopeContext[] {projectScope, new InstanceScope(), new DefaultScope()};
+ }
+ }
+
+ boolean originalEnableIgnore = fPreferenceService.getBoolean(
+ getPreferenceNodeQualifier(), HTMLCorePreferenceNames.IGNORE_ATTRIBUTE_NAMES,
+ HTMLCorePreferenceNames.IGNORE_ATTRIBUTE_NAMES_DEFAULT, fLookupOrder);
+
+ String originalAttributeNames = fPreferenceService.getString(
+ getPreferenceNodeQualifier(), HTMLCorePreferenceNames.ATTRIBUTE_NAMES_TO_IGNORE,
+ HTMLCorePreferenceNames.ATTRIBUTE_NAMES_TO_IGNORE_DEFAULT, fLookupOrder);
+
+ StringBuffer ignoreList = new StringBuffer(originalAttributeNames);
+
+ if (ignoreList.length() > 0)
+ ignoreList.append(',');
+
+ ignoreList.append(fPattern);
+
+ fLookupOrder[0].getNode(getPreferenceNodeQualifier())
+ .putBoolean(HTMLCorePreferenceNames.IGNORE_ATTRIBUTE_NAMES, true);
+
+ fLookupOrder[0].getNode(getPreferenceNodeQualifier())
+ .put(HTMLCorePreferenceNames.ATTRIBUTE_NAMES_TO_IGNORE, ignoreList.toString());
+
+ for(int i = 0; i < fLookupOrder.length; i++) {
+ try {
+ fLookupOrder[i].getNode(getPreferenceNodeQualifier()).flush();
+ } catch (BackingStoreException e) {
+ Logger.logException(e);
+ }
+ }
+
+ PreferenceDialog dialog = hasProjectSettings ?
+ PreferencesUtil.createPropertyDialogOn(getShell(), project, HTMLValidationPreferencePage.PROPERTY_PAGE_ID, null, null) :
+ PreferencesUtil.createPreferenceDialogOn(getShell(), HTMLValidationPreferencePage.PREFERENCE_PAGE_ID, null, null);
+ if (dialog != null) {
+ Object page = dialog.getSelectedPage();
+ if (page instanceof HTMLValidationPreferencePage) {
+ ((HTMLValidationPreferencePage)page).overrideOriginValues(originalEnableIgnore, originalAttributeNames);
+ }
+ dialog.open();
+ }
+ }
+
+ /*
+ * @see ICompletionProposal#getDisplayString()
+ */
+ public String getDisplayString() {
+ if (fDisplayString != null)
+ return fDisplayString;
+ return ""; //$NON-NLS-1$
+ }
+
+ /*
+ * @see ICompletionProposal#getAdditionalProposalInfo()
+ */
+ public String getAdditionalProposalInfo() {
+ return fAdditionalProposalInfo;
+ }
+
+ /*
+ * @see ICompletionProposal#getImage()
+ */
+ public Image getImage() {
+ return HTMLEditorPluginImageHelper.getInstance().getImage(HTMLEditorPluginImages.IMG_DTOOL_DO_NOT_VALIDATE);
+ }
+
+ /*
+ * @see ICompletionProposal#getSelection(IDocument)
+ */
+ public Point getSelection(IDocument document) {
+ return new Point(fReplacementOffset /*+ fCursorPosition*/, 0);
+ }
+
+ /*
+ * @see ICompletionProposal#getContextInformation()
+ */
+ public IContextInformation getContextInformation() {
+ return fContextInformation;
+ }
+
+ /*
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ public boolean equals(Object obj) {
+ if (obj instanceof IgnoreAttributeNameCompletionProposal) {
+ IgnoreAttributeNameCompletionProposal p = (IgnoreAttributeNameCompletionProposal)obj;
+ return (this.fPattern.equals(p.fPattern) && this.fTarget == p.fTarget && this.fReplacementOffset == p.fReplacementOffset);
+ }
+ return false;
+ }
+
+ private String getPreferenceNodeQualifier() {
+ return HTMLCorePlugin.getDefault().getBundle().getSymbolicName();
+ }
+
+ private String getProjectSettingsKey() {
+ return HTMLCorePreferenceNames.USE_PROJECT_SETTINGS;
+ }
+
+ private Shell getShell() {
+ IWorkbench workBench = HTMLUIPlugin.getDefault().getWorkbench();
+ IWorkbenchWindow workBenchWindow = workBench == null ? null : workBench.getActiveWorkbenchWindow();
+ return workBenchWindow == null ? null : workBenchWindow.getShell();
+ }
+}