[248380] Provide Sort option in Outline view
diff --git a/bundles/org.eclipse.wst.xml.ui/icons/full/obj16/sort.gif b/bundles/org.eclipse.wst.xml.ui/icons/full/obj16/sort.gif
new file mode 100644
index 0000000..23c5d0b
--- /dev/null
+++ b/bundles/org.eclipse.wst.xml.ui/icons/full/obj16/sort.gif
Binary files differ
diff --git a/bundles/org.eclipse.wst.xml.ui/src/org/eclipse/wst/xml/ui/internal/XMLUIMessages.java b/bundles/org.eclipse.wst.xml.ui/src/org/eclipse/wst/xml/ui/internal/XMLUIMessages.java
index fe5a3b8..3150a14 100644
--- a/bundles/org.eclipse.wst.xml.ui/src/org/eclipse/wst/xml/ui/internal/XMLUIMessages.java
+++ b/bundles/org.eclipse.wst.xml.ui/src/org/eclipse/wst/xml/ui/internal/XMLUIMessages.java
@@ -300,7 +300,8 @@
 	public static String previousSibling_label;
 	public static String previousSibling_description;
 	public static String Use_XInclude;
-
+	public static String Open;
+	public static String _UI_BUTTON_SORT;
 
 	static {
 		// load message values from bundle file
diff --git a/bundles/org.eclipse.wst.xml.ui/src/org/eclipse/wst/xml/ui/internal/XMLUIPluginResources.properties b/bundles/org.eclipse.wst.xml.ui/src/org/eclipse/wst/xml/ui/internal/XMLUIPluginResources.properties
index 323ab87..aed3ba8 100644
--- a/bundles/org.eclipse.wst.xml.ui/src/org/eclipse/wst/xml/ui/internal/XMLUIPluginResources.properties
+++ b/bundles/org.eclipse.wst.xml.ui/src/org/eclipse/wst/xml/ui/internal/XMLUIPluginResources.properties
@@ -329,4 +329,5 @@
 previousSibling_description=Go to Previous Sibling
 gotoMatchingTag_label=Matching Tag
 gotoMatchingTag_description=Go To Matching Tag
-
+Open=Open "{0}"
+_UI_BUTTON_SORT=Sort
diff --git a/bundles/org.eclipse.wst.xml.ui/src/org/eclipse/wst/xml/ui/internal/contentoutline/JFaceNodeLabelProvider.java b/bundles/org.eclipse.wst.xml.ui/src/org/eclipse/wst/xml/ui/internal/contentoutline/JFaceNodeLabelProvider.java
index 8e3b504..6168dde 100644
--- a/bundles/org.eclipse.wst.xml.ui/src/org/eclipse/wst/xml/ui/internal/contentoutline/JFaceNodeLabelProvider.java
+++ b/bundles/org.eclipse.wst.xml.ui/src/org/eclipse/wst/xml/ui/internal/contentoutline/JFaceNodeLabelProvider.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2001, 2006 IBM Corporation and others.
+ * Copyright (c) 2001, 2008 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
@@ -12,16 +12,16 @@
  *******************************************************************************/
 package org.eclipse.wst.xml.ui.internal.contentoutline;
 
-import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.jface.viewers.ColumnLabelProvider;
 import org.eclipse.swt.graphics.Image;
 import org.eclipse.wst.sse.core.internal.provisional.INodeAdapter;
 import org.eclipse.wst.sse.core.internal.provisional.INodeNotifier;
 import org.eclipse.wst.sse.ui.internal.contentoutline.IJFaceNodeAdapter;
 
 /**
- * A label provider backed by JFaceNodeAdapters.
+ * A (column) label provider backed by JFaceNodeAdapters.
  */
-public class JFaceNodeLabelProvider extends LabelProvider {
+public class JFaceNodeLabelProvider extends ColumnLabelProvider {
 	/**
 	 * JFaceNodeLabelProvider constructor comment.
 	 */
diff --git a/bundles/org.eclipse.wst.xml.ui/src/org/eclipse/wst/xml/ui/internal/contentoutline/RefreshStructureJob.java b/bundles/org.eclipse.wst.xml.ui/src/org/eclipse/wst/xml/ui/internal/contentoutline/RefreshStructureJob.java
index 6960dbd..a58c09b 100644
--- a/bundles/org.eclipse.wst.xml.ui/src/org/eclipse/wst/xml/ui/internal/contentoutline/RefreshStructureJob.java
+++ b/bundles/org.eclipse.wst.xml.ui/src/org/eclipse/wst/xml/ui/internal/contentoutline/RefreshStructureJob.java
@@ -13,7 +13,9 @@
 package org.eclipse.wst.xml.ui.internal.contentoutline;
 
 import java.util.ArrayList;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
 import org.eclipse.core.runtime.IProgressMonitor;
 import org.eclipse.core.runtime.IStatus;
@@ -42,32 +44,53 @@
 
 	/** debug flag */
 	static final boolean DEBUG;
-	private static final long UPDATE_DELAY = 250;
+	private static final long UPDATE_DELAY = 300;
 	static {
 		String value = Platform.getDebugOption("org.eclipse.wst.sse.ui/debug/refreshStructure"); //$NON-NLS-1$
 		DEBUG = (value != null) && value.equalsIgnoreCase("true"); //$NON-NLS-1$
 	}
 	/** List of refresh requests (Nodes) */
-	private final List fRequests;
+	private final List fRefreshes;
+	/** List of update requests (Nodes) */
+	private final List fUpdates;
+	/** List of update requests (Nodes) */
+	private final List fUpdateProperties;
 	/** the structured viewers */
-	List fViewers = new ArrayList(3);
+	Set fRefreshViewers = new HashSet(3);
+	Set fUpdateViewers = new HashSet(3);
 
 	public RefreshStructureJob() {
 		super(XMLUIMessages.refreshoutline_0);
 		setPriority(Job.LONG);
 		setSystem(true);
-		fRequests = new ArrayList(2);
+		fRefreshes = new ArrayList(5);
+		fUpdates = new ArrayList(5);
+		fUpdateProperties = new ArrayList(5);
 	}
 
-	private synchronized void addRequest(Node newNodeRequest) {
+	private synchronized void addUpdateRequest(Node newNodeRequest, String[] updateProperties) {
+		/*
+		 * If we get to here, either from existing request list being zero
+		 * length, or no exisitng requests "matched" new request, then add the
+		 * new request.
+		 */
+		fUpdates.add(newNodeRequest);
+		fUpdateProperties.add(updateProperties);
+	}
+
+	private synchronized void addUpdateViewer(StructuredViewer viewer) {
+		fUpdateViewers.add(viewer);
+	}
+
+	private synchronized void addRefreshRequest(Node newNodeRequest) {
 		/*
 		 * note: the caller must NOT pass in null node request (which, since
 		 * private method, we do not need to gaurd against here, as long as we
 		 * gaurd against it in calling method.
 		 */
-		int size = fRequests.size();
+		int size = fRefreshes.size();
 		for (int i = 0; i < size; i++) {
-			Node existingNodeRequest = (Node) fRequests.get(i);
+			Node existingNodeRequest = (Node) fRefreshes.get(i);
 			/*
 			 * https://bugs.eclipse.org/bugs/show_bug.cgi?id=157427 If we
 			 * already have a request which equals the new request, discard
@@ -94,7 +117,7 @@
 			 * any that are contained by new request ... in future :) .
 			 */
 			if (contains(newNodeRequest, existingNodeRequest)) {
-				fRequests.set(i, newNodeRequest);
+				fRefreshes.set(i, newNodeRequest);
 				return;
 			}
 		}
@@ -103,13 +126,11 @@
 		 * length, or no exisitng requests "matched" new request, then add the
 		 * new request.
 		 */
-		fRequests.add(newNodeRequest);
+		fRefreshes.add(newNodeRequest);
 	}
 
-	private synchronized void addViewer(StructuredViewer viewer) {
-		if (!fViewers.contains(viewer)) {
-			fViewers.add(viewer);
-		}
+	private synchronized void addRefreshViewer(StructuredViewer viewer) {
+		fRefreshViewers.add(viewer);
 	}
 
 	/**
@@ -203,23 +224,70 @@
 	}
 
 	/**
-	 * This method also synchronized because it accesses the fRequests queue
-	 * and fViewers list
+	 * Update must be on UI thread because it's on a SWT widget.
+	 * 
+	 * @param node
+	 */
+	private void doUpdate(final StructuredViewer[] viewers, final Node node, final String[] properties) {
+		final Display display = PlatformUI.getWorkbench().getDisplay();
+		display.asyncExec(new Runnable() {
+			public void run() {
+				if (DEBUG) {
+					System.out.println("refresh on: [" + node.getNodeName() + "]"); //$NON-NLS-1$ //$NON-NLS-2$
+				}
+
+				for (int i = 0; i < viewers.length; i++) {
+					if (!viewers[i].getControl().isDisposed()) {
+						viewers[i].update(node, properties);
+					}
+					else {
+						if (DEBUG) {
+							System.out.println("   !!! skipped refreshing disposed viewer: " + viewers[i]); //$NON-NLS-1$
+						}
+					}
+				}
+			}
+		});
+	}
+
+	/**
+	 * This method also synchronized because it accesses the fRefreshes queue
+	 * and fRefreshViewers list
 	 * 
 	 * @return an array containing and array of the currently requested Nodes
 	 *         to refresh and the viewers in which to refresh them
 	 */
-	private synchronized Object[] getRequests() {
-		Node[] toRefresh = (Node[]) fRequests.toArray(new Node[fRequests.size()]);
-		fRequests.clear();
+	private synchronized Object[] getRefreshRequests() {
+		Node[] toRefresh = (Node[]) fRefreshes.toArray(new Node[fRefreshes.size()]);
+		fRefreshes.clear();
 
-		StructuredViewer[] viewers = (StructuredViewer[]) fViewers.toArray(new StructuredViewer[fViewers.size()]);
-		fViewers.clear();
+		StructuredViewer[] viewers = (StructuredViewer[]) fRefreshViewers.toArray(new StructuredViewer[fRefreshViewers.size()]);
+		fRefreshViewers.clear();
 
 		return new Object[]{toRefresh, viewers};
 	}
 
 	/**
+	 * This method also synchronized because it accesses the fUpdates queue
+	 * and fUpdateViewers list
+	 * 
+	 * @return an array containing and array of the currently requested Nodes
+	 *         to refresh and the viewers in which to refresh them
+	 */
+	private synchronized Object[] getUpdateRequests() {
+		Node[] toUpdate = (Node[]) fUpdates.toArray(new Node[fUpdates.size()]);
+		fUpdates.clear();
+
+		StructuredViewer[] viewers = (StructuredViewer[]) fUpdateViewers.toArray(new StructuredViewer[fUpdateViewers.size()]);
+		fUpdateViewers.clear();
+
+		String[][] properties = (String[][]) fUpdateProperties.toArray(new String[fUpdateProperties.size()][]);
+		fUpdateProperties.clear();
+
+		return new Object[]{toUpdate, viewers, properties};
+	}
+
+	/**
 	 * Invoke a refresh on the viewer on the given node.
 	 * 
 	 * @param node
@@ -229,25 +297,32 @@
 			return;
 		}
 
-		addViewer(viewer);
-		addRequest(node);
+		addRefreshViewer(viewer);
+		addRefreshRequest(node);
+		schedule(UPDATE_DELAY);
+	}
+
+	/**
+	 * Invoke a refresh on the viewer on the given node.
+	 * 
+	 * @param node
+	 */
+	public void update(StructuredViewer viewer, Node node, String[] properties) {
+		if (node == null) {
+			return;
+		}
+
+		addUpdateViewer(viewer);
+		addUpdateRequest(node, properties);
 		schedule(UPDATE_DELAY);
 	}
 
 	protected IStatus run(IProgressMonitor monitor) {
 		IStatus status = Status.OK_STATUS;
 		try {
-			// Retrieve BOTH viewers and Nodes on one block
-			Object[] requests = getRequests();
-			Node[] nodes = (Node[]) requests[0];
-			StructuredViewer[] viewers = (StructuredViewer[]) requests[1];
-
-			for (int i = 0; i < nodes.length; i++) {
-				if (monitor.isCanceled()) {
-					throw new OperationCanceledException();
-				}
-				doRefresh(nodes[i], viewers);
-			}
+			performUpdates();
+			
+			performRefreshes(monitor);
 		}
 		finally {
 			monitor.done();
@@ -255,4 +330,30 @@
 		return status;
 	}
 
+	private void performRefreshes(IProgressMonitor monitor) {
+		// Retrieve BOTH viewers and Nodes on one block
+		Object[] requests = getRefreshRequests();
+		Node[] nodes = (Node[]) requests[0];
+		StructuredViewer[] viewers = (StructuredViewer[]) requests[1];
+
+		for (int i = 0; i < nodes.length; i++) {
+			if (monitor.isCanceled()) {
+				throw new OperationCanceledException();
+			}
+			doRefresh(nodes[i], viewers);
+		}
+	}
+
+	private void performUpdates() {
+		// Retrieve BOTH viewers and Nodes on one block
+		Object[] requests = getUpdateRequests();
+		Node[] nodes = (Node[]) requests[0];
+		StructuredViewer[] viewers = (StructuredViewer[]) requests[1];
+		String[][] properties = (String[][]) requests[2];
+
+		for (int i = 0; i < nodes.length; i++) {
+			doUpdate(viewers, nodes[i], properties[i]);
+		}
+	}
+
 }
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 3e2a63e..a13408d 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,6 +41,7 @@
 	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_SORT = "icons/full/obj16/sort.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_obj.gif"; //$NON-NLS-1$    
 	public static final String IMG_OBJ_TAG_GENERIC_EMPHASIZED = "icons/full/obj16/tag_generic_emphasized_obj.gif"; //$NON-NLS-1$        
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 d71a7a3..771ff17 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
@@ -65,6 +65,21 @@
 	 */
 	public static final String USE_INFERRED_GRAMMAR = getUseInferredGrammarKey();
 
+	public static class OUTLINE_BEHAVIOR {
+		public static final String ELEMENT_NODE = "outline-behavior.element-node"; //$NON-NLS-1$
+		public static final String ATTRIBUTE_NODE = "outline-behavior.attribute-node"; //$NON-NLS-1$
+		public static final String TEXT_NODE = "outline-behavior.text-node"; //$NON-NLS-1$
+		public static final String CDATA_SECTION_NODE = "outline-behavior.cdata-section-node"; //$NON-NLS-1$
+		public static final String ENTITY_REFERENCE_NODE = "outline-behavior.entity-reference-node"; //$NON-NLS-1$
+		public static final String ENTITY_NODE = "outline-behavior.entity-node"; //$NON-NLS-1$
+		public static final String PROCESSING_INSTRUCTION_NODE = "outline-behavior.instruction-node"; //$NON-NLS-1$
+		public static final String COMMENT_NODE = "outline-behavior.comment-node"; //$NON-NLS-1$
+		public static final String DOCUMENT_NODE = "outline-behavior.document-node"; //$NON-NLS-1$
+		public static final String DOCUMENT_TYPE_NODE = "outline-behavior.document-type-node"; //$NON-NLS-1$
+		public static final String DOCUMENT_FRAGMENT_NODE = "outline-behavior.document-fragment-node"; //$NON-NLS-1$
+		public static final String NOTATION_NODE = "outline-behavior.notation-node"; //$NON-NLS-1$
+	}
+	
 	private static String getUseInferredGrammarKey() {
 		return "useInferredGrammar"; //$NON-NLS-1$
 	}
diff --git a/bundles/org.eclipse.wst.xml.ui/src/org/eclipse/wst/xml/ui/views/contentoutline/AbstractXMLContentOutlineConfiguration.java b/bundles/org.eclipse.wst.xml.ui/src/org/eclipse/wst/xml/ui/views/contentoutline/AbstractXMLContentOutlineConfiguration.java
index abeda1c..c536274 100644
--- a/bundles/org.eclipse.wst.xml.ui/src/org/eclipse/wst/xml/ui/views/contentoutline/AbstractXMLContentOutlineConfiguration.java
+++ b/bundles/org.eclipse.wst.xml.ui/src/org/eclipse/wst/xml/ui/views/contentoutline/AbstractXMLContentOutlineConfiguration.java
@@ -50,7 +50,7 @@
  * @see org.eclipse.wst.sse.ui.views.contentoutline.ContentOutlineConfiguration
  * @since 3.1
  */
-public class AbstractXMLContentOutlineConfiguration extends ContentOutlineConfiguration {
+public abstract class AbstractXMLContentOutlineConfiguration extends ContentOutlineConfiguration {
 	private class ActionManagerMenuListener implements IMenuListener, IReleasable {
 		private XMLNodeActionManager fActionManager;
 		private TreeViewer fTreeViewer;
diff --git a/bundles/org.eclipse.wst.xml.ui/src/org/eclipse/wst/xml/ui/views/contentoutline/SortAction.java b/bundles/org.eclipse.wst.xml.ui/src/org/eclipse/wst/xml/ui/views/contentoutline/SortAction.java
new file mode 100644
index 0000000..14caaa9
--- /dev/null
+++ b/bundles/org.eclipse.wst.xml.ui/src/org/eclipse/wst/xml/ui/views/contentoutline/SortAction.java
@@ -0,0 +1,124 @@
+/*******************************************************************************

+ * Copyright (c) 2001, 2008 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

+ *     Jens Lukowski/Innoopract - initial renaming/restructuring

+ *     

+ *******************************************************************************/

+package org.eclipse.wst.xml.ui.views.contentoutline;

+

+import org.eclipse.jface.preference.IPreferenceStore;

+import org.eclipse.jface.viewers.TreeViewer;

+import org.eclipse.jface.viewers.ViewerComparator;

+import org.eclipse.wst.sse.core.utils.StringUtils;

+import org.eclipse.wst.sse.ui.internal.contentoutline.PropertyChangeUpdateAction;

+import org.eclipse.wst.xml.ui.internal.XMLUIMessages;

+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.w3c.dom.Element;

+import org.w3c.dom.Node;

+

+

+/**

+ * Toggling sort action for XML outlines

+ */

+class SortAction extends PropertyChangeUpdateAction {

+	static class ViewerNodeComparator extends ViewerComparator {

+		private int[] categories = new int[12];

+

+		public ViewerNodeComparator(IPreferenceStore store) {

+			super();

+			initCategoryOrder(store);

+		}

+

+		/**

+		 * @param store

+		 */

+		private void initCategoryOrder(IPreferenceStore store) {

+			String[] order = new String[12];

+			order[Node.ELEMENT_NODE - 1] = StringUtils.unpack(store.getString(XMLUIPreferenceNames.OUTLINE_BEHAVIOR.ELEMENT_NODE))[0];

+			order[Node.ATTRIBUTE_NODE - 1] = StringUtils.unpack(store.getString(XMLUIPreferenceNames.OUTLINE_BEHAVIOR.ATTRIBUTE_NODE))[0];

+			order[Node.TEXT_NODE - 1] = StringUtils.unpack(store.getString(XMLUIPreferenceNames.OUTLINE_BEHAVIOR.TEXT_NODE))[0];

+			order[Node.CDATA_SECTION_NODE - 1] = StringUtils.unpack(store.getString(XMLUIPreferenceNames.OUTLINE_BEHAVIOR.CDATA_SECTION_NODE))[0];

+			order[Node.ENTITY_REFERENCE_NODE - 1] = StringUtils.unpack(store.getString(XMLUIPreferenceNames.OUTLINE_BEHAVIOR.ENTITY_REFERENCE_NODE))[0];

+			order[Node.ENTITY_NODE - 1] = StringUtils.unpack(store.getString(XMLUIPreferenceNames.OUTLINE_BEHAVIOR.ENTITY_NODE))[0];

+			order[Node.PROCESSING_INSTRUCTION_NODE - 1] = StringUtils.unpack(store.getString(XMLUIPreferenceNames.OUTLINE_BEHAVIOR.PROCESSING_INSTRUCTION_NODE))[0];

+			order[Node.COMMENT_NODE - 1] = StringUtils.unpack(store.getString(XMLUIPreferenceNames.OUTLINE_BEHAVIOR.COMMENT_NODE))[0];

+			order[Node.DOCUMENT_NODE - 1] = StringUtils.unpack(store.getString(XMLUIPreferenceNames.OUTLINE_BEHAVIOR.DOCUMENT_NODE))[0];

+			order[Node.DOCUMENT_TYPE_NODE - 1] = StringUtils.unpack(store.getString(XMLUIPreferenceNames.OUTLINE_BEHAVIOR.DOCUMENT_TYPE_NODE))[0];

+			order[Node.DOCUMENT_FRAGMENT_NODE - 1] = StringUtils.unpack(store.getString(XMLUIPreferenceNames.OUTLINE_BEHAVIOR.DOCUMENT_FRAGMENT_NODE))[0];

+			order[Node.NOTATION_NODE - 1] = StringUtils.unpack(store.getString(XMLUIPreferenceNames.OUTLINE_BEHAVIOR.NOTATION_NODE))[0];

+			for (int i = 0; i < order.length; i++) {

+				try {

+					categories[i] = Integer.parseInt(order[i]);

+				}

+				catch (NumberFormatException e) {

+				}

+			}

+		}

+

+		public int category(Object element) {

+			if (element instanceof Node) {

+				return (categories[((Node) element).getNodeType() - 1]);

+			}

+

+			return super.category(element);

+		}

+

+		/*

+		 * (non-Javadoc)

+		 * 

+		 * @see

+		 * org.eclipse.jface.viewers.ViewerComparator#isSorterProperty(java

+		 * .lang.Object, java.lang.String)

+		 */

+		public boolean isSorterProperty(Object element, String property) {

+			if (element instanceof Element) {

+				return ((Element) element).hasAttribute(property);

+			}

+			return super.isSorterProperty(element, property);

+		}

+	}

+

+	private TreeViewer treeViewer;

+

+	public SortAction(TreeViewer viewer, IPreferenceStore store, String preferenceKey) {

+		super(XMLUIMessages._UI_BUTTON_SORT, store, preferenceKey, false); //$NON-NLS-1$

+		setImageDescriptor(XMLEditorPluginImageHelper.getInstance().getImageDescriptor(XMLEditorPluginImages.IMG_OBJ_SORT));

+		setToolTipText(getText());

+		treeViewer = viewer;

+		if (isChecked()) {

+			treeViewer.setComparator(createComparator());

+		}

+	}

+

+	private ViewerNodeComparator createComparator() {

+		return new ViewerNodeComparator(getPreferenceStore());

+	}

+

+	/*

+	 * (non-Javadoc)

+	 * 

+	 * @see org.eclipse.ui.texteditor.IUpdate#update()

+	 */

+	public void update() {

+		super.update();

+		treeViewer.getControl().setRedraw(false);

+		Object[] expandedElements = treeViewer.getExpandedElements();

+		if (isChecked()) {

+			treeViewer.setComparator(createComparator());

+		}

+		else {

+			treeViewer.setComparator(null);

+		}

+		treeViewer.setInput(treeViewer.getInput());

+		treeViewer.setExpandedElements(expandedElements);

+		treeViewer.getControl().setRedraw(true);

+	}

+}

diff --git a/bundles/org.eclipse.wst.xml.ui/src/org/eclipse/wst/xml/ui/views/contentoutline/XMLContentOutlineConfiguration.java b/bundles/org.eclipse.wst.xml.ui/src/org/eclipse/wst/xml/ui/views/contentoutline/XMLContentOutlineConfiguration.java
index af64219..f1e5bb1 100644
--- a/bundles/org.eclipse.wst.xml.ui/src/org/eclipse/wst/xml/ui/views/contentoutline/XMLContentOutlineConfiguration.java
+++ b/bundles/org.eclipse.wst.xml.ui/src/org/eclipse/wst/xml/ui/views/contentoutline/XMLContentOutlineConfiguration.java
@@ -38,24 +38,28 @@
 import org.eclipse.wst.xml.ui.internal.XMLUIMessages;
 import org.eclipse.wst.xml.ui.internal.contentoutline.JFaceNodeContentProvider;
 import org.eclipse.wst.xml.ui.internal.contentoutline.JFaceNodeLabelProvider;
+import org.eclipse.wst.xml.ui.internal.preferences.XMLUIPreferenceNames;
 import org.w3c.dom.Attr;
 import org.w3c.dom.Element;
 import org.w3c.dom.NamedNodeMap;
 import org.w3c.dom.Node;
 
 /**
- * Outline Configuration for generic XML support, expects that the viewer's
+ * More advanced Outline Configuration for XML support.  Expects that the viewer's
  * input will be the DOM Model.
  * 
- * @see org.eclipse.wst.sse.ui.views.contentoutline.ContentOutlineConfiguration
+ * @see AbstractXMLContentOutlineConfiguration
  * @since 1.0
-
- TODO: Add Sort and Hide Comment actions
-
  */
 public class XMLContentOutlineConfiguration extends AbstractXMLContentOutlineConfiguration {
+	static final String ATTR_NAME = "name";
+	static final String ATTR_ID = "id";
 
 	private class AttributeShowingLabelProvider extends JFaceNodeLabelProvider {
+		public boolean isLabelProperty(Object element, String property) {
+			return true;
+		}
+
 		/*
 		 * (non-Javadoc)
 		 * 
@@ -64,9 +68,9 @@
 		public String getText(Object o) {
 			StringBuffer text = null;
 			if (o instanceof Node) {
-				text = new StringBuffer(super.getText(o));
 				Node node = (Node) o;
 				if ((node.getNodeType() == Node.ELEMENT_NODE) && fShowAttributes) {
+					text = new StringBuffer(super.getText(o));
 					// https://bugs.eclipse.org/bugs/show_bug.cgi?id=88444
 					if (node.hasAttributes()) {
 						Element element = (Element) node;
@@ -115,8 +119,8 @@
 										requiredAttribute = attr;
 									}
 									else {
-										hasId = hasId || attrName.equals("id"); //$NON-NLS-1$
-										hasName = hasName || attrName.equals("name"); //$NON-NLS-1$
+										hasId = hasId || attrName.equals(ATTR_ID);
+										hasName = hasName || attrName.equals(ATTR_NAME);
 									}
 								}
 								++i;
@@ -135,10 +139,10 @@
 							shownAttribute = requiredAttribute;
 						}
 						else if (hasId) {
-							shownAttribute = attributes.getNamedItem("id"); //$NON-NLS-1$
+							shownAttribute = attributes.getNamedItem(ATTR_ID);
 						}
 						else if (hasName) {
-							shownAttribute = attributes.getNamedItem("name"); //$NON-NLS-1$
+							shownAttribute = attributes.getNamedItem(ATTR_NAME);
 						}
 						if (shownAttribute == null) {
 							shownAttribute = attributes.item(0);
@@ -157,12 +161,65 @@
 						}
 					}
 				}
+				else {
+					text = new StringBuffer(super.getText(o));
+				}
 			}
 			else {
 				return super.toString();
 			}
 			return text.toString();
 		}
+
+		/* (non-Javadoc)
+		 * @see org.eclipse.jface.viewers.CellLabelProvider#getToolTipText(java.lang.Object)
+		 */
+		public String getToolTipText(Object element) {
+			if (element instanceof Node) {
+				switch (((Node) element).getNodeType()) {
+					case Node.COMMENT_NODE :
+					case Node.CDATA_SECTION_NODE :
+					case Node.PROCESSING_INSTRUCTION_NODE :
+					case Node.TEXT_NODE : {
+						String nodeValue = ((Node) element).getNodeValue().trim();
+						return prepareText(nodeValue);
+					}
+					case Node.ELEMENT_NODE : {
+						// show the preceding comment's tooltip information
+						Node previous = ((Node) element).getPreviousSibling();
+						if (previous != null && previous.getNodeType() == Node.TEXT_NODE)
+							previous = previous.getPreviousSibling();
+						if (previous != null && previous.getNodeType() == Node.COMMENT_NODE)
+							return getToolTipText(previous);
+					}
+				}
+			}
+			return super.getToolTipText(element);
+		}
+
+		/**
+		 * Remove leading indentation from each line in the give string.
+		 * @param text
+		 * @return
+		 */
+		private String prepareText(String text) {
+			StringBuffer nodeText = new StringBuffer();
+			for (int i = 0; i < text.length(); i++) {
+				char c = text.charAt(i);
+				if (c != '\r' && c != '\n') {
+					nodeText.append(c);
+				}
+				else if (c == '\r' || c == '\n') {
+					nodeText.append('\n');
+					while (Character.isWhitespace(c) && i < text.length()) {
+						i++;
+						c = text.charAt(i);
+					}
+					nodeText.append(c);
+				}
+			}
+			return nodeText.toString();
+		}
 	}
 
 	/**
@@ -199,6 +256,7 @@
 			fTreeViewer.refresh(true);
 		}
 	}
+	
 	private ILabelProvider fAttributeShowingLabelProvider;
 	private IContentProvider fContentProvider = null;
 
@@ -207,7 +265,11 @@
 	/*
 	 * Preference key for Show Attributes
 	 */
-	private final String OUTLINE_SHOW_ATTRIBUTE_PREF = "outline-show-attribute-editor"; //$NON-NLS-1$
+	private final String OUTLINE_SHOW_ATTRIBUTE_PREF = "outline-show-attribute"; //$NON-NLS-1$
+	/*
+	 * Preference key for Sorting
+	 */
+	private final String OUTLINE_SORT_PREF = "outline-sort"; //$NON-NLS-1$
 
 	/**
 	 * Create new instance of XMLContentOutlineConfiguration
@@ -215,6 +277,37 @@
 	public XMLContentOutlineConfiguration() {
 		// Must have empty constructor to createExecutableExtension
 		super();
+
+		/**
+		 * Set up our preference store here. This is done so that subclasses
+		 * aren't required to set their own values, although if they have,
+		 * those will be used instead.
+		 */
+		IPreferenceStore store = getPreferenceStore();
+		if (store.getDefaultString(XMLUIPreferenceNames.OUTLINE_BEHAVIOR.DOCUMENT_NODE).length() == 0)
+			store.setDefault(XMLUIPreferenceNames.OUTLINE_BEHAVIOR.DOCUMENT_NODE, "1, true");
+		if (store.getDefaultString(XMLUIPreferenceNames.OUTLINE_BEHAVIOR.PROCESSING_INSTRUCTION_NODE).length() == 0)
+			store.setDefault(XMLUIPreferenceNames.OUTLINE_BEHAVIOR.PROCESSING_INSTRUCTION_NODE, "2, true");
+		if (store.getDefaultString(XMLUIPreferenceNames.OUTLINE_BEHAVIOR.DOCUMENT_TYPE_NODE).length() == 0)
+			store.setDefault(XMLUIPreferenceNames.OUTLINE_BEHAVIOR.DOCUMENT_TYPE_NODE, "3, true");
+		if (store.getDefaultString(XMLUIPreferenceNames.OUTLINE_BEHAVIOR.DOCUMENT_FRAGMENT_NODE).length() == 0)
+			store.setDefault(XMLUIPreferenceNames.OUTLINE_BEHAVIOR.DOCUMENT_FRAGMENT_NODE, "4, true");
+		if (store.getDefaultString(XMLUIPreferenceNames.OUTLINE_BEHAVIOR.COMMENT_NODE).length() == 0)
+			store.setDefault(XMLUIPreferenceNames.OUTLINE_BEHAVIOR.COMMENT_NODE, "5, true");
+		if (store.getDefaultString(XMLUIPreferenceNames.OUTLINE_BEHAVIOR.ATTRIBUTE_NODE).length() == 0)
+			store.setDefault(XMLUIPreferenceNames.OUTLINE_BEHAVIOR.ATTRIBUTE_NODE, "6, false");
+		if (store.getDefaultString(XMLUIPreferenceNames.OUTLINE_BEHAVIOR.ELEMENT_NODE).length() == 0)
+			store.setDefault(XMLUIPreferenceNames.OUTLINE_BEHAVIOR.ELEMENT_NODE, "7, true");
+		if (store.getDefaultString(XMLUIPreferenceNames.OUTLINE_BEHAVIOR.ENTITY_REFERENCE_NODE).length() == 0)
+			store.setDefault(XMLUIPreferenceNames.OUTLINE_BEHAVIOR.ENTITY_REFERENCE_NODE, "8, true");
+		if (store.getDefaultString(XMLUIPreferenceNames.OUTLINE_BEHAVIOR.CDATA_SECTION_NODE).length() == 0)
+			store.setDefault(XMLUIPreferenceNames.OUTLINE_BEHAVIOR.CDATA_SECTION_NODE, "9, true");
+		if (store.getDefaultString(XMLUIPreferenceNames.OUTLINE_BEHAVIOR.ENTITY_NODE).length() == 0)
+			store.setDefault(XMLUIPreferenceNames.OUTLINE_BEHAVIOR.ENTITY_NODE, "10, true");
+		if (store.getDefaultString(XMLUIPreferenceNames.OUTLINE_BEHAVIOR.NOTATION_NODE).length() == 0)
+			store.setDefault(XMLUIPreferenceNames.OUTLINE_BEHAVIOR.NOTATION_NODE, "11, true");
+		if (store.getDefaultString(XMLUIPreferenceNames.OUTLINE_BEHAVIOR.TEXT_NODE).length() == 0)
+			store.setDefault(XMLUIPreferenceNames.OUTLINE_BEHAVIOR.TEXT_NODE, "12, false");
 	}
 
 	/*
@@ -226,14 +319,18 @@
 		IContributionItem[] items;
 		// https://bugs.eclipse.org/bugs/show_bug.cgi?id=88444
 		IContributionItem showAttributeItem = new PropertyChangeUpdateActionContributionItem(new ToggleShowAttributeAction(getPreferenceStore(), OUTLINE_SHOW_ATTRIBUTE_PREF, viewer));
+
+		IContributionItem sortItem = new PropertyChangeUpdateActionContributionItem(new SortAction(viewer, getPreferenceStore(), OUTLINE_SORT_PREF));
+
 		items = super.createMenuContributions(viewer);
 		if (items == null) {
-			items = new IContributionItem[]{showAttributeItem};
+			items = new IContributionItem[]{showAttributeItem, sortItem};
 		}
 		else {
-			IContributionItem[] combinedItems = new IContributionItem[items.length + 1];
+			IContributionItem[] combinedItems = new IContributionItem[items.length + 2];
 			System.arraycopy(items, 0, combinedItems, 0, items.length);
 			combinedItems[items.length] = showAttributeItem;
+			combinedItems[items.length+1] = sortItem;
 			items = combinedItems;
 		}
 		return items;
@@ -255,7 +352,7 @@
 	protected void enableShowAttributes(boolean showAttributes, TreeViewer treeViewer) {
 		// nothing by default
 	}
-
+	
 	/*
 	 * (non-Javadoc)
 	 * 
@@ -271,12 +368,13 @@
 	private Object getFilteredNode(Object object) {
 		if (object instanceof Node) {
 			Node node = (Node) object;
-	
+			short nodeType = node.getNodeType();
 			// replace attribute node in selection with its parent
-			if (node.getNodeType() == Node.ATTRIBUTE_NODE) {
+			if (nodeType == Node.ATTRIBUTE_NODE) {
 				node = ((Attr) node).getOwnerElement();
 			}
-			else if (node.getNodeType() == Node.TEXT_NODE) {
+			// anything else not visible, replace with parent node
+			else if (nodeType == Node.TEXT_NODE) {
 				node = node.getParentNode();
 			}
 			return node;
@@ -317,5 +415,4 @@
 		}
 		return filteredSelection;
 	}
-
 }