[91866] Schema-based Content Assist should be stricter - fix to emphasize 'stictly valid' suggestions
diff --git a/bundles/org.eclipse.wst.xml.ui/icons/full/obj16/tag-generic-deemphasized.gif b/bundles/org.eclipse.wst.xml.ui/icons/full/obj16/tag-generic-deemphasized.gif
new file mode 100644
index 0000000..73f47e8
--- /dev/null
+++ b/bundles/org.eclipse.wst.xml.ui/icons/full/obj16/tag-generic-deemphasized.gif
Binary files differ
diff --git a/bundles/org.eclipse.wst.xml.ui/icons/full/obj16/tag-generic-emphasized.gif b/bundles/org.eclipse.wst.xml.ui/icons/full/obj16/tag-generic-emphasized.gif
new file mode 100644
index 0000000..bd1ba54
--- /dev/null
+++ b/bundles/org.eclipse.wst.xml.ui/icons/full/obj16/tag-generic-emphasized.gif
Binary files differ
diff --git a/bundles/org.eclipse.wst.xml.ui/src/org/eclipse/wst/xml/ui/internal/contentassist/AbstractContentAssistProcessor.java b/bundles/org.eclipse.wst.xml.ui/src/org/eclipse/wst/xml/ui/internal/contentassist/AbstractContentAssistProcessor.java
index 2ffc03e..4e5da01 100644
--- a/bundles/org.eclipse.wst.xml.ui/src/org/eclipse/wst/xml/ui/internal/contentassist/AbstractContentAssistProcessor.java
+++ b/bundles/org.eclipse.wst.xml.ui/src/org/eclipse/wst/xml/ui/internal/contentassist/AbstractContentAssistProcessor.java
@@ -63,9 +63,11 @@
 import org.eclipse.wst.xml.core.internal.regions.DOMRegionContext;
 import org.eclipse.wst.xml.ui.internal.Logger;
 import org.eclipse.wst.xml.ui.internal.XMLUIMessages;
+import org.eclipse.wst.xml.ui.internal.XMLUIPlugin;
 import org.eclipse.wst.xml.ui.internal.editor.CMImageUtil;
 import org.eclipse.wst.xml.ui.internal.editor.XMLEditorPluginImageHelper;
 import org.eclipse.wst.xml.ui.internal.editor.XMLEditorPluginImages;
+import org.eclipse.wst.xml.ui.internal.preferences.XMLUIPreferenceNames;
 import org.eclipse.wst.xml.ui.internal.taginfo.MarkupTagInfoProvider;
 import org.w3c.dom.Attr;
 import org.w3c.dom.Document;
@@ -761,7 +763,6 @@
 	protected void addTagInsertionProposals(ContentAssistRequest contentAssistRequest, int childPosition) {
 		List cmnodes = null;
 		Node parent = contentAssistRequest.getParent();
-		List validActions = null;
 		String error = null;
 
 		// CMVC #242943 shouldn't have proposals before XMLPI
@@ -815,10 +816,16 @@
 				addPCDATAProposal(parentDecl.getNodeName(), contentAssistRequest);
 			}
 			else {
-				// retrieve the list of children
-				validActions = getAvailableChildrenAtIndex((Element) parent, childPosition);
-				cmnodes = getValidCMNodes(childPosition, ModelQueryAction.INSERT, validActions);
-				Iterator nodeIterator = cmnodes.iterator();
+				// retrieve the list of all possible children within this parent context
+				cmnodes = getAvailableChildElementDeclarations((Element)parent, childPosition, ModelQueryAction.INSERT);
+                
+                // retrieve the list of the possible children within this parent context and at this index
+                List strictCMNodeSuggestions = null;
+                if (XMLUIPreferenceNames.SUGGESTION_STRATEGY_VALUE_STRICT.equals(XMLUIPlugin.getInstance().getPreferenceStore().getString(XMLUIPreferenceNames.SUGGESTION_STRATEGY))) 
+                { 
+                  strictCMNodeSuggestions = getValidChildElementDeclarations((Element)parent, childPosition, ModelQueryAction.INSERT);                
+                }
+ 				Iterator nodeIterator = cmnodes.iterator();
 				if (!nodeIterator.hasNext()) {
 					if (getCMElementDeclaration(parent) != null)
 						error = NLS.bind(XMLUIMessages._Has_no_available_child, (new Object[]{parent.getNodeName()}));
@@ -838,9 +845,20 @@
 						// only add proposals for the child element's that
 						// begin with the matchstring
 						String tagname = getRequiredName(parent, elementDecl);
-						Image image = CMImageUtil.getImage(elementDecl);
+                        boolean isStrictCMNodeSuggestion = strictCMNodeSuggestions != null ? strictCMNodeSuggestions.contains(elementDecl) : false;
+                        
+                        Image image = CMImageUtil.getImage(elementDecl);
+  
 						if (image == null) {
-							image = XMLEditorPluginImageHelper.getInstance().getImage(XMLEditorPluginImages.IMG_OBJ_TAG_GENERIC);
+                            if (strictCMNodeSuggestions != null) {
+                                image = isStrictCMNodeSuggestion ?                                 
+							            XMLEditorPluginImageHelper.getInstance().getImage(XMLEditorPluginImages.IMG_OBJ_TAG_GENERIC_EMPHASIZED) :
+                                        XMLEditorPluginImageHelper.getInstance().getImage(XMLEditorPluginImages.IMG_OBJ_TAG_GENERIC_DEEMPHASIZED);                                                                                        
+                            }
+                            else {
+                                image = XMLEditorPluginImageHelper.getInstance().getImage(XMLEditorPluginImages.IMG_OBJ_TAG_GENERIC);
+                            }
+                              
 						}
 						// Account for the < and >. If attributes were
 						// added, the cursor will be placed
@@ -850,7 +868,8 @@
 						if (beginsWith(tagname, matchString)) {
 							String proposedText = getRequiredText(parent, elementDecl);
 							String proposedInfo = getAdditionalInfo(parentDecl, elementDecl);
-							CustomCompletionProposal proposal = new CustomCompletionProposal(proposedText, contentAssistRequest.getReplacementBeginPosition(), contentAssistRequest.getReplacementLength(), markupAdjustment, image, tagname, null, proposedInfo, XMLRelevanceConstants.R_TAG_INSERTION);
+                            int relevance = isStrictCMNodeSuggestion ? XMLRelevanceConstants.R_STICTLY_VALID_TAG_INSERTION : XMLRelevanceConstants.R_TAG_INSERTION; 
+							CustomCompletionProposal proposal = new CustomCompletionProposal(proposedText, contentAssistRequest.getReplacementBeginPosition(), contentAssistRequest.getReplacementLength(), markupAdjustment, image, tagname, null, proposedInfo, relevance);
 							contentAssistRequest.addProposal(proposal);
 						}
 					}
@@ -913,13 +932,12 @@
 		List cmnodes = null;
 		Node parent = contentAssistRequest.getParent();
 		IDOMNode node = (IDOMNode) contentAssistRequest.getNode();
-		List validActions = null;
 		String error = null;
 		String matchString = contentAssistRequest.getMatchString();
 		if (parent.getNodeType() == Node.ELEMENT_NODE) {
 			// retrieve the list of children
-			validActions = getAvailableChildrenAtIndex((Element) parent, childPosition);
-			cmnodes = getValidCMNodes(childPosition, ModelQueryAction.INSERT, validActions);
+			//validActions = getAvailableChildrenAtIndex((Element) parent, childPosition);
+			cmnodes = getAvailableChildElementDeclarations((Element)parent, childPosition, ModelQueryAction.INSERT);
 			Iterator nodeIterator = cmnodes.iterator();
 			// chop off any leading <'s and whitespace from the matchstring
 			while ((matchString.length() > 0) && (Character.isWhitespace(matchString.charAt(0)) || beginsWith(matchString, "<"))) //$NON-NLS-1$
@@ -1596,7 +1614,7 @@
 	}
 
 	// returns a list of ModelQueryActions
-	protected List getAvailableChildrenAtIndex(Element parent, int index) {
+	protected List getAvailableChildrenAtIndex(Element parent, int index, int validityChecking) {
 		List list = new ArrayList();
 		CMElementDeclaration parentDecl = getCMElementDeclaration(parent);
 		if (parentDecl != null) {
@@ -1605,8 +1623,7 @@
 			// int editMode = modelQuery.getEditMode();
 			int editMode = ModelQuery.EDIT_MODE_UNCONSTRAINED;
 			int ic = (editMode == ModelQuery.EDIT_MODE_CONSTRAINED_STRICT) ? ModelQuery.INCLUDE_CHILD_NODES | ModelQuery.INCLUDE_SEQUENCE_GROUPS : ModelQuery.INCLUDE_CHILD_NODES;
-			int vc = (editMode == ModelQuery.EDIT_MODE_CONSTRAINED_STRICT) ? ModelQuery.VALIDITY_STRICT : ModelQuery.VALIDITY_NONE;
-			modelQuery.getInsertActions(parent, parentDecl, index, ic, vc, list);
+			modelQuery.getInsertActions(parent, parentDecl, index, ic, validityChecking, list);
 		}
 		return list;
 	}
@@ -2043,7 +2060,18 @@
 		return name;
 	}
 
-	protected List getValidCMNodes(int childPosition, int kindOfAction, List modelQueryActions) {
+    // returns a list of CMNodes that are available within this parent context
+    // Given the grammar shown below and a snippet of XML code (where the '|' indicated the cursor position) 
+    // the list would return all of the element declarations that are potential child elements of Foo. 
+    //
+    // grammar : Foo -> (A, B, C)   
+    // snippet : <Foo><A>|
+    // result  : {A, B, C}
+    // 
+    // TODO cs... do we need to pass in the 'kindOfAction'?  Seems to me we could assume it's always an insert.
+	protected List getAvailableChildElementDeclarations(Element parent, int childPosition, int kindOfAction) 
+    {
+        List modelQueryActions =  getAvailableChildrenAtIndex(parent, childPosition, ModelQuery.VALIDITY_NONE); 
 		Iterator iterator = modelQueryActions.iterator();
 		List cmnodes = new Vector();
 		while (iterator.hasNext()) {
@@ -2057,6 +2085,30 @@
 		return cmnodes;
 	}
 
+    // returns a list of CMNodes that can be validly inserted at this childPosition 
+    // Given the grammar shown below and a snippet of XML code (where the '|' indicated the cursor position) 
+    // the list would return only the element declarations can be inserted while maintaing validity of the content. 
+    //
+    // grammar : Foo -> (A, B, C)   
+    // snippet : <Foo><A>|
+    // result  : {B}
+    //    
+    protected List getValidChildElementDeclarations(Element parent, int childPosition, int kindOfAction) 
+    {
+        List modelQueryActions =  getAvailableChildrenAtIndex(parent, childPosition, ModelQuery.VALIDITY_STRICT); 
+        Iterator iterator = modelQueryActions.iterator();
+        List cmnodes = new Vector();
+        while (iterator.hasNext()) {
+            ModelQueryAction action = (ModelQueryAction) iterator.next();      
+            if (childPosition < 0 || (action.getStartIndex() <= childPosition && childPosition <= action.getEndIndex()) && action.getKind() == kindOfAction) {
+                CMNode actionCMNode = action.getCMNode();
+                if (actionCMNode != null && !cmnodes.contains(actionCMNode))
+                    cmnodes.add(actionCMNode);
+            }           
+        }
+        return cmnodes;
+    }
+
 	/**
 	 * Similar to the call in HTMLContentAssistProcessor. Pass in a node, it
 	 * tells you if the document is XML type.
diff --git a/bundles/org.eclipse.wst.xml.ui/src/org/eclipse/wst/xml/ui/internal/contentassist/XMLRelevanceConstants.java b/bundles/org.eclipse.wst.xml.ui/src/org/eclipse/wst/xml/ui/internal/contentassist/XMLRelevanceConstants.java
index 34896ce..ae6dd76 100644
--- a/bundles/org.eclipse.wst.xml.ui/src/org/eclipse/wst/xml/ui/internal/contentassist/XMLRelevanceConstants.java
+++ b/bundles/org.eclipse.wst.xml.ui/src/org/eclipse/wst/xml/ui/internal/contentassist/XMLRelevanceConstants.java
@@ -44,6 +44,7 @@
 	// CMVC 246618
 	int R_REQUIRED = 10;
 	int R_TAG_INSERTION = 500;
+    int R_STICTLY_VALID_TAG_INSERTION = 600;
 	int R_TAG_NAME = 1200;
 	int R_XML_ATTRIBUTE_NAME = 900;
 	int R_XML_ATTRIBUTE_VALUE = 800;
diff --git a/bundles/org.eclipse.wst.xml.ui/src/org/eclipse/wst/xml/ui/internal/editor/XMLEditorPluginImages.java b/bundles/org.eclipse.wst.xml.ui/src/org/eclipse/wst/xml/ui/internal/editor/XMLEditorPluginImages.java
index 8160f77..fbb4ad7 100644
--- a/bundles/org.eclipse.wst.xml.ui/src/org/eclipse/wst/xml/ui/internal/editor/XMLEditorPluginImages.java
+++ b/bundles/org.eclipse.wst.xml.ui/src/org/eclipse/wst/xml/ui/internal/editor/XMLEditorPluginImages.java
@@ -41,7 +41,9 @@
 	public static final String IMG_OBJ_LOCAL_VARIABLE = "icons/full/obj16/localvariable_obj.gif"; //$NON-NLS-1$
 	public static final String IMG_OBJ_NOTATION = "icons/full/obj16/notation.gif"; //$NON-NLS-1$
 	public static final String IMG_OBJ_PROCESSINGINSTRUCTION = "icons/full/obj16/proinst_obj.gif"; //$NON-NLS-1$
-	public static final String IMG_OBJ_TAG_GENERIC = "icons/full/obj16/tag-generic.gif"; //$NON-NLS-1$
+    public static final String IMG_OBJ_TAG_GENERIC = "icons/full/obj16/tag-generic.gif"; //$NON-NLS-1$
+    public static final String IMG_OBJ_TAG_GENERIC_DEEMPHASIZED = "icons/full/obj16/tag-generic-deemphasized.gif"; //$NON-NLS-1$    
+    public static final String IMG_OBJ_TAG_GENERIC_EMPHASIZED = "icons/full/obj16/tag-generic-emphasized.gif"; //$NON-NLS-1$        
 	public static final String IMG_OBJ_TAG_MACRO = "icons/full/obj16/tag-macro.gif"; //$NON-NLS-1$
 	public static final String IMG_OBJ_TXTEXT = "icons/full/obj16/text.gif"; //$NON-NLS-1$
 	public static final String IMG_OBJ_WARNING_OBJ = "icons/full/obj16/warning_obj.gif"; //$NON-NLS-1$
diff --git a/bundles/org.eclipse.wst.xml.ui/src/org/eclipse/wst/xml/ui/internal/preferences/XMLSourcePreferencePage.java b/bundles/org.eclipse.wst.xml.ui/src/org/eclipse/wst/xml/ui/internal/preferences/XMLSourcePreferencePage.java
index 31a8fdf..4d47b0a 100644
--- a/bundles/org.eclipse.wst.xml.ui/src/org/eclipse/wst/xml/ui/internal/preferences/XMLSourcePreferencePage.java
+++ b/bundles/org.eclipse.wst.xml.ui/src/org/eclipse/wst/xml/ui/internal/preferences/XMLSourcePreferencePage.java
@@ -19,6 +19,7 @@
 import org.eclipse.swt.events.SelectionListener;
 import org.eclipse.swt.layout.GridData;
 import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Combo;
 import org.eclipse.swt.widgets.Composite;
 import org.eclipse.swt.widgets.Control;
 import org.eclipse.swt.widgets.Group;
@@ -42,6 +43,7 @@
 	protected Button fAutoPropose;
 	protected Label fAutoProposeLabel;
 	protected Text fAutoProposeText;
+    protected Combo fSuggestionStrategyCombo;
 	protected Button fClearAllBlankLines;
 
 	// Formatting
@@ -78,6 +80,12 @@
 
 		fAutoProposeLabel = createLabel(contentAssistGroup, XMLUIMessages.Prompt_when_these_characte_UI_);
 		fAutoProposeText = createTextField(contentAssistGroup);
+        
+        createLabel(contentAssistGroup, "Suggestion strategy:");
+        fSuggestionStrategyCombo = new Combo(contentAssistGroup, SWT.READ_ONLY);
+        fSuggestionStrategyCombo.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+        fSuggestionStrategyCombo.add("Lax");
+        fSuggestionStrategyCombo.add("Strict");
 	}
 
 	protected void createContentsForFormattingGroup(Composite parent) {
@@ -156,7 +164,8 @@
 	protected void initializeValuesForContentAssistGroup() {
 		// Content Assist
 		fAutoPropose.setSelection(getPreferenceStore().getBoolean(XMLUIPreferenceNames.AUTO_PROPOSE));
-		fAutoProposeText.setText(getPreferenceStore().getString(XMLUIPreferenceNames.AUTO_PROPOSE_CODE));
+		fAutoProposeText.setText(getPreferenceStore().getString(XMLUIPreferenceNames.AUTO_PROPOSE_CODE)); 
+        fSuggestionStrategyCombo.setText(getPreferenceStore().getString(XMLUIPreferenceNames.SUGGESTION_STRATEGY));
 	}
 
 	protected void initializeValuesForFormattingGroup() {
@@ -195,6 +204,9 @@
 		// Content Assist
 		fAutoPropose.setSelection(getPreferenceStore().getDefaultBoolean(XMLUIPreferenceNames.AUTO_PROPOSE));
 		fAutoProposeText.setText(getPreferenceStore().getDefaultString(XMLUIPreferenceNames.AUTO_PROPOSE_CODE));
+        
+        // TODO.. (cs) we need to map the preference value to a translated name
+        fSuggestionStrategyCombo.setText(getPreferenceStore().getDefaultString(XMLUIPreferenceNames.SUGGESTION_STRATEGY));
 	}
 
 	protected void performDefaultsForFormattingGroup() {
@@ -235,6 +247,7 @@
 		// Content Assist
 		getPreferenceStore().setValue(XMLUIPreferenceNames.AUTO_PROPOSE, fAutoPropose.getSelection());
 		getPreferenceStore().setValue(XMLUIPreferenceNames.AUTO_PROPOSE_CODE, fAutoProposeText.getText());
+        getPreferenceStore().setValue(XMLUIPreferenceNames.SUGGESTION_STRATEGY, fSuggestionStrategyCombo.getText());
 	}
 
 	protected void storeValuesForFormattingGroup() {
diff --git a/bundles/org.eclipse.wst.xml.ui/src/org/eclipse/wst/xml/ui/internal/preferences/XMLUIPreferenceInitializer.java b/bundles/org.eclipse.wst.xml.ui/src/org/eclipse/wst/xml/ui/internal/preferences/XMLUIPreferenceInitializer.java
index 31a9706..fb6253a 100644
--- a/bundles/org.eclipse.wst.xml.ui/src/org/eclipse/wst/xml/ui/internal/preferences/XMLUIPreferenceInitializer.java
+++ b/bundles/org.eclipse.wst.xml.ui/src/org/eclipse/wst/xml/ui/internal/preferences/XMLUIPreferenceInitializer.java
@@ -21,7 +21,7 @@
 		
 		store.setDefault(XMLUIPreferenceNames.AUTO_PROPOSE, true);
 		store.setDefault(XMLUIPreferenceNames.AUTO_PROPOSE_CODE, "<"); //$NON-NLS-1$
-
+        store.setDefault(XMLUIPreferenceNames.SUGGESTION_STRATEGY, XMLUIPreferenceNames.SUGGESTION_STRATEGY_VALUE_LAX);
 		store.setDefault(
 					XMLUIPreferenceNames.USE_INFERRED_GRAMMAR, true);
 
diff --git a/bundles/org.eclipse.wst.xml.ui/src/org/eclipse/wst/xml/ui/internal/preferences/XMLUIPreferenceNames.java b/bundles/org.eclipse.wst.xml.ui/src/org/eclipse/wst/xml/ui/internal/preferences/XMLUIPreferenceNames.java
index a4e1e76..e0bda81 100644
--- a/bundles/org.eclipse.wst.xml.ui/src/org/eclipse/wst/xml/ui/internal/preferences/XMLUIPreferenceNames.java
+++ b/bundles/org.eclipse.wst.xml.ui/src/org/eclipse/wst/xml/ui/internal/preferences/XMLUIPreferenceNames.java
@@ -15,6 +15,9 @@
  * Preference keys for XML UI
  */
 public class XMLUIPreferenceNames {
+  
+    public final static String SUGGESTION_STRATEGY_VALUE_LAX = "Lax";
+    public final static String SUGGESTION_STRATEGY_VALUE_STRICT = "Strict";  
 	/**
 	 * A named preference that controls if code assist gets auto activated.
 	 * <p>
@@ -65,4 +68,18 @@
 	private static String getUseInferredGrammarKey() {
 		return "useInferredGrammar"; //$NON-NLS-1$
 	}
+    
+    /**
+     * A named preference that holds the characters that auto activate code
+     * assist.
+     * <p>
+     * Value is of type <code>String</code>. All characters that trigger
+     * auto code assist.
+     * </p>
+     */
+    public static final String SUGGESTION_STRATEGY = getSuggestionStrategeyKey();
+
+    private static String getSuggestionStrategeyKey() {
+        return "suggestionStrategy";//$NON-NLS-1$
+    }    
 }