bug 253753: show namespaces and details about the element in the
properties view

Signed-off-by: Florian Thienel <florian@thienel.org>
diff --git a/org.eclipse.vex.core.tests/src/org/eclipse/vex/core/internal/dom/NamespaceTest.java b/org.eclipse.vex.core.tests/src/org/eclipse/vex/core/internal/dom/NamespaceTest.java
index 558deaa..ed546d5 100644
--- a/org.eclipse.vex.core.tests/src/org/eclipse/vex/core/internal/dom/NamespaceTest.java
+++ b/org.eclipse.vex.core.tests/src/org/eclipse/vex/core/internal/dom/NamespaceTest.java
@@ -314,4 +314,33 @@
 

 		assertEquals(inputContent, outputContent);

 	}

+	

+	@Test

+	public void allVisibleNamespacePrefixes() throws Exception {

+		final Element parent = new Element("parent");

+		final Element element = new Element("element");

+		final Element child = new Element("child");

+		parent.addChild(element);

+		parent.declareDefaultNamespace("http://namespace/uri/parent/default");

+		parent.declareNamespace("ns1", "http://namespace/uri/1");

+		parent.declareNamespace("ns2", "http://namespace/uri/2");

+		element.addChild(child);

+		element.declareNamespace("ns3", "http://namespace/uri/3");

+		element.declareNamespace("ns1", "http://namespace/uri/1a");

+		child.declareDefaultNamespace("http://namespace/uri/child/default");

+		

+		assertEquals(2, parent.getNamespacePrefixes().size());

+		assertTrue(parent.getNamespacePrefixes().contains("ns1"));

+		assertTrue(parent.getNamespacePrefixes().contains("ns2"));

+		

+		assertEquals(3, element.getNamespacePrefixes().size());

+		assertTrue(element.getNamespacePrefixes().contains("ns1"));

+		assertTrue(element.getNamespacePrefixes().contains("ns2"));

+		assertTrue(element.getNamespacePrefixes().contains("ns3"));

+		

+		assertEquals(3, child.getNamespacePrefixes().size());

+		assertTrue(child.getNamespacePrefixes().contains("ns1"));

+		assertTrue(child.getNamespacePrefixes().contains("ns2"));

+		assertTrue(child.getNamespacePrefixes().contains("ns3"));

+	}

 }

diff --git a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/dom/Element.java b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/dom/Element.java
index 5640308..eb95d37 100644
--- a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/dom/Element.java
+++ b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/dom/Element.java
@@ -15,6 +15,7 @@
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
@@ -266,49 +267,6 @@
 		return sb.toString();
 	}
 
-	// ========================================================= PRIVATE
-
-	private class AttributeChangeEdit implements IUndoableEdit {
-
-		private QualifiedName name;
-		private String oldValue;
-		private String newValue;
-
-		public AttributeChangeEdit(QualifiedName name, String oldValue, String newValue) {
-			this.name = name;
-			this.oldValue = oldValue;
-			this.newValue = newValue;
-		}
-
-		public boolean combine(IUndoableEdit edit) {
-			return false;
-		}
-
-		public void undo() throws CannotUndoException {
-			Document doc = (Document) getDocument();
-			try {
-				doc.setUndoEnabled(false);
-				setAttribute(name, oldValue);
-			} catch (DocumentValidationException ex) {
-				throw new CannotUndoException();
-			} finally {
-				doc.setUndoEnabled(true);
-			}
-		}
-
-		public void redo() throws CannotRedoException {
-			Document doc = (Document) getDocument();
-			try {
-				doc.setUndoEnabled(false);
-				setAttribute(name, newValue);
-			} catch (DocumentValidationException ex) {
-				throw new CannotUndoException();
-			} finally {
-				doc.setUndoEnabled(true);
-			}
-		}
-	}
-
 	public void setParent(Element parent) {
 		this.parent = parent;
 	}
@@ -353,6 +311,14 @@
 		return result;
 	}
 	
+	public Collection<String> getNamespacePrefixes() {
+		final HashSet<String> result = new HashSet<String>();
+		result.addAll(getDeclaredNamespacePrefixes());
+		if (parent != null)
+			result.addAll(parent.getNamespacePrefixes());
+		return result;
+	}
+	
 	public void declareNamespace(final String namespacePrefix, final String namespaceURI) {
 		if (namespaceURI == null || "".equals(namespaceURI.trim()))
 			return;
@@ -376,16 +342,6 @@
 		return "Element";
 	}
 
-	/**
-	 * Sets the content of the node
-	 * 
-	 * @param content
-	 *            Content object holding the node's content
-	 * @param startOffset
-	 *            offset at which the node's content starts
-	 * @param endOffset
-	 *            offset at which the node's content ends
-	 */
 	public void setContent(Content content, int startOffset, int endOffset) {
 		super.setContent(content, startOffset, endOffset);
 	}
@@ -401,4 +357,46 @@
 			return getDocument().getBaseURI();
 		return null;
 	}
+	
+	private class AttributeChangeEdit implements IUndoableEdit {
+
+		private QualifiedName name;
+		private String oldValue;
+		private String newValue;
+
+		public AttributeChangeEdit(QualifiedName name, String oldValue, String newValue) {
+			this.name = name;
+			this.oldValue = oldValue;
+			this.newValue = newValue;
+		}
+
+		public boolean combine(IUndoableEdit edit) {
+			return false;
+		}
+
+		public void undo() throws CannotUndoException {
+			Document doc = (Document) getDocument();
+			try {
+				doc.setUndoEnabled(false);
+				setAttribute(name, oldValue);
+			} catch (DocumentValidationException ex) {
+				throw new CannotUndoException();
+			} finally {
+				doc.setUndoEnabled(true);
+			}
+		}
+
+		public void redo() throws CannotRedoException {
+			Document doc = (Document) getDocument();
+			try {
+				doc.setUndoEnabled(false);
+				setAttribute(name, newValue);
+			} catch (DocumentValidationException ex) {
+				throw new CannotUndoException();
+			} finally {
+				doc.setUndoEnabled(true);
+			}
+		}
+	}
+
 }
diff --git a/org.eclipse.vex.ui/src/org/eclipse/vex/ui/internal/property/ElementPropertySource.java b/org.eclipse.vex.ui/src/org/eclipse/vex/ui/internal/property/ElementPropertySource.java
index 78ceb93..174ec37 100644
--- a/org.eclipse.vex.ui/src/org/eclipse/vex/ui/internal/property/ElementPropertySource.java
+++ b/org.eclipse.vex.ui/src/org/eclipse/vex/ui/internal/property/ElementPropertySource.java
@@ -1,98 +1,145 @@
 /*******************************************************************************
- * Copyright (c) 2004, 2008 John Krasnay and others.
+ * Copyright (c) 2004, 2011 John Krasnay 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:
- *     John Krasnay - initial API and implementation
+ *     John Krasnay    - initial API and implementation
+ *     Florian Thienel - namespace support (bug 253753)
  *******************************************************************************/
 package org.eclipse.vex.ui.internal.property;
 
+import java.util.ArrayList;
 import java.util.List;
 
 import org.eclipse.ui.views.properties.ComboBoxPropertyDescriptor;
 import org.eclipse.ui.views.properties.IPropertyDescriptor;
+import org.eclipse.ui.views.properties.IPropertySheetEntry;
 import org.eclipse.ui.views.properties.IPropertySource2;
 import org.eclipse.ui.views.properties.PropertyDescriptor;
 import org.eclipse.ui.views.properties.TextPropertyDescriptor;
 import org.eclipse.vex.core.internal.dom.Attribute;
 import org.eclipse.vex.core.internal.dom.DocumentValidationException;
 import org.eclipse.vex.core.internal.dom.Element;
+import org.eclipse.vex.core.internal.dom.Namespace;
 import org.eclipse.vex.core.internal.dom.Validator;
 import org.eclipse.vex.core.internal.validator.AttributeDefinition;
 import org.eclipse.vex.ui.internal.editor.Messages;
 
-/**
- * Property source that treats element attributes as properties.
- */
 public class ElementPropertySource implements IPropertySource2 {
 
-	/**
-	 * Class constructor.
-	 * 
-	 * @param element
-	 *            Selected element.
-	 * @param validator
-	 *            Validator used to get attribute definitions for the element.
-	 * @param multi
-	 *            True if multiple elements are selected. In this case the "id"
-	 *            attribute will not be editable.
-	 */
-	public ElementPropertySource(final Element element, final Validator validator, final boolean multi) {
+	private static final String ATTR_ID = "id"; //$NON-NLS-1$
+	
+	private static final String ELEMENT_NAME_PROPERTY = "elementName"; //$NON-NLS-1$
+	private static final String ELEMENT_NAMESPACE_URI_PROPERTY = "elementNsUri"; //$NON-NLS-1$
+	private static final String ELEMENT_NAMESPACE_PREFIX_PROPERTY = "elementNsPrefix"; //$NON-NLS-1$
+	
+	private static final String ELEMENT_CATEGORY = "Element";
+	private static final String ATTRIBUTES_CATEGORY = "Attributes";
+	private static final String NAMESPACES_CATEGORY = "Namespaces";
+
+	private final Element element;
+	private final Validator validator;
+	private final boolean multipleElementsSelected;
+
+	public ElementPropertySource(final Element element, final Validator validator, final boolean multipleElementsSelected) {
 		this.element = element;
 		this.validator = validator;
-		this.multi = multi;
+		this.multipleElementsSelected = multipleElementsSelected;
 	}
 
 	public Object getEditableValue() {
-		// TODO Auto-generated method stub
 		return null;
 	}
 
 	public IPropertyDescriptor[] getPropertyDescriptors() {
-		// note that elements from DocumentFragments don't have access
-		// to their original document, so we get it from the VexWidget
-		final List<AttributeDefinition> attrDefs = validator.getAttributeDefinitions(element);
-		final IPropertyDescriptor[] pds = new IPropertyDescriptor[attrDefs.size()];
-		for (int i = 0; i < attrDefs.size(); i++) {
-			final AttributeDefinition def = attrDefs.get(i);
-			if (multi && def.getName().equals(ATTR_ID))
-				pds[i] = new PropertyDescriptor(def, def.getName());
-			else if (def.isFixed())
-				pds[i] = new PropertyDescriptor(def, def.getName());
-			else if (def.getType() == AttributeDefinition.Type.ENUMERATION)
-				pds[i] = new ComboBoxPropertyDescriptor(def, def.getName(), getEnumValues(def));
-			else
-				pds[i] = new TextPropertyDescriptor(def, def.getName());
+		final List<IPropertyDescriptor> result = new ArrayList<IPropertyDescriptor>();
+		
+		result.add(createExpertPropertyDescriptor(ELEMENT_NAME_PROPERTY, "Element Name", ELEMENT_CATEGORY));
+		
+		if (element.getQualifiedName().getQualifier() != null) {
+			result.add(createExpertPropertyDescriptor(ELEMENT_NAMESPACE_URI_PROPERTY, "Namespace URI", ELEMENT_CATEGORY));
+			result.add(createExpertPropertyDescriptor(ELEMENT_NAMESPACE_PREFIX_PROPERTY, "Namespace Prefix", ELEMENT_CATEGORY));
 		}
-		return pds;
+
+		/*
+		 * Note that elements from DocumentFragments don't have access to
+		 * their original document, so we get it from the VexWidget.
+		 */
+		final List<AttributeDefinition> attributeDefinitions = validator.getAttributeDefinitions(element);
+		for (final AttributeDefinition attributeDefinition : attributeDefinitions) {
+			final PropertyDescriptor propertyDescriptor;
+			if (multipleElementsSelected && attributeDefinition.getName().equals(ATTR_ID))
+				propertyDescriptor = new PropertyDescriptor(attributeDefinition, attributeDefinition.getName());
+			else if (attributeDefinition.isFixed())
+				propertyDescriptor = new PropertyDescriptor(attributeDefinition, attributeDefinition.getName());
+			else if (attributeDefinition.getType() == AttributeDefinition.Type.ENUMERATION)
+				propertyDescriptor = new ComboBoxPropertyDescriptor(attributeDefinition, attributeDefinition.getName(), getEnumValues(attributeDefinition));
+			else
+				propertyDescriptor = new TextPropertyDescriptor(attributeDefinition, attributeDefinition.getName());
+			propertyDescriptor.setCategory(ATTRIBUTES_CATEGORY);
+			result.add(propertyDescriptor);
+		}
+
+		for (final String namespacePrefix : element.getNamespacePrefixes()) {
+			final String namespaceUri = element.getNamespaceURI(namespacePrefix);
+			final NamespaceUri namespaceDeclaration = new NamespaceUri(namespaceUri);
+			result.add(createExpertPropertyDescriptor(namespaceDeclaration, Namespace.XMLNS_NAMESPACE_PREFIX + ":" + namespacePrefix, NAMESPACES_CATEGORY));
+		}
+
+		final String defaultNamespaceUri = element.getDefaultNamespaceURI();
+		if (defaultNamespaceUri != null) {
+			final NamespaceUri namespaceDeclaration = new NamespaceUri(defaultNamespaceUri);
+			result.add(createExpertPropertyDescriptor(namespaceDeclaration, Namespace.XMLNS_NAMESPACE_PREFIX, NAMESPACES_CATEGORY));
+		}
+
+		return result.toArray(new IPropertyDescriptor[result.size()]);
+	}
+	
+	private IPropertyDescriptor createExpertPropertyDescriptor(final Object id, final String displayName, final String category) {
+		final PropertyDescriptor propertyDescriptor = new PropertyDescriptor(id, displayName);
+		propertyDescriptor.setCategory(category);
+		propertyDescriptor.setFilterFlags(new String[] { IPropertySheetEntry.FILTER_ID_EXPERT });
+		return propertyDescriptor;
 	}
 
 	public Object getPropertyValue(final Object id) {
-		if (!(id instanceof AttributeDefinition))
-			return "";
-		final AttributeDefinition attributeDefinition = (AttributeDefinition) id;
-		if (multi && id.equals(ATTR_ID))
-			return Messages.getString("ElementPropertySource.multiple"); //$NON-NLS-1$
+		if (id == ELEMENT_NAME_PROPERTY)
+			return element.getLocalName();
+		if (id == ELEMENT_NAMESPACE_URI_PROPERTY)
+			return element.getQualifiedName().getQualifier();
+		if (id == ELEMENT_NAMESPACE_PREFIX_PROPERTY)
+			return element.getNamespacePrefix(element.getQualifiedName().getQualifier());
+		
+		if (id instanceof AttributeDefinition) {
+			final AttributeDefinition attributeDefinition = (AttributeDefinition) id;
+			if (multipleElementsSelected && id.equals(ATTR_ID))
+				return Messages.getString("ElementPropertySource.multiple"); //$NON-NLS-1$
 
-		final Attribute attribute = element.getAttribute(attributeDefinition.getName());
-		final String value;
-		if (attribute != null)
-			value = attribute.getValue();
-		else
-			value = nullToEmpty(attributeDefinition.getDefaultValue());
+			final Attribute attribute = element.getAttribute(attributeDefinition.getName());
+			final String value;
+			if (attribute != null)
+				value = attribute.getValue();
+			else
+				value = nullToEmpty(attributeDefinition.getDefaultValue());
 
-		if (attributeDefinition.getType() == AttributeDefinition.Type.ENUMERATION) {
-			final String[] values = getEnumValues(attributeDefinition);
-			for (int i = 0; i < values.length; i++)
-				if (values[i].equals(value))
-					return Integer.valueOf(i);
-			return Integer.valueOf(0);
-			// TODO: If the actual value is not in the list, we should probably add it.
+			if (attributeDefinition.getType() == AttributeDefinition.Type.ENUMERATION) {
+				final String[] values = getEnumValues(attributeDefinition);
+				for (int i = 0; i < values.length; i++)
+					if (values[i].equals(value))
+						return Integer.valueOf(i);
+				return Integer.valueOf(0);
+				// TODO: If the actual value is not in the list, we should probably add it.
+			}
+			return value;
 		}
-		return value;
+		
+		if (id instanceof NamespaceUri) 
+			return ((NamespaceUri) id).uri;
+
+		return "";
 	}
 
 	private static String nullToEmpty(final String string) {
@@ -102,24 +149,39 @@
 	}
 
 	public boolean isPropertySet(final Object id) {
-		// TODO Auto-generated method stub
+		if (id == ELEMENT_NAME_PROPERTY)
+			return true;
+		if (id == ELEMENT_NAMESPACE_URI_PROPERTY)
+			return true;
+		if (id == ELEMENT_NAMESPACE_PREFIX_PROPERTY)
+			return true;
+		
+		if (id instanceof AttributeDefinition) {
+			final AttributeDefinition attributeDefinition = (AttributeDefinition) id;
+			final Attribute attribute = element.getAttribute(attributeDefinition.getName());
+			if (attribute == null)
+				return false;
+			return true;
+		}
+		
+		if (id instanceof NamespaceUri)
+			return true;
+		
 		return false;
 	}
 
 	public void resetPropertyValue(final Object id) {
-		// TODO Auto-generated method stub
-
+		if (!(id instanceof AttributeDefinition))
+			return;
+		final AttributeDefinition attributeDefinition = (AttributeDefinition) id;
+		element.removeAttribute(attributeDefinition.getName());
 	}
 
 	public void setPropertyValue(final Object id, final Object value) {
 		if (!(id instanceof AttributeDefinition))
 			return;
-		/*
-		 * Note that elements from DocumentFragments don't have access to
-		 * their original document, so we get it from the VexWidget.
-		 */
 		final AttributeDefinition attributeDefinition = (AttributeDefinition) id;
-		
+
 		try {
 			if (attributeDefinition.getType() == AttributeDefinition.Type.ENUMERATION) {
 				final int i = ((Integer) value).intValue();
@@ -139,22 +201,9 @@
 		}
 	}
 
-	public boolean isPropertyResettable(final Object id) {
-		// TODO Auto-generated method stub
-		return true;
-	}
-
-	// ======================================== PRIVATE
-
-	private static final String ATTR_ID = "id"; //$NON-NLS-1$
-
-	private final Element element;
-	private final Validator validator;
-	private final boolean multi;
-
-	private String[] getEnumValues(final AttributeDefinition def) {
-		String[] values = def.getValues();
-		if (def.isRequired())
+	private static String[] getEnumValues(final AttributeDefinition attributeDefinition) {
+		String[] values = attributeDefinition.getValues();
+		if (attributeDefinition.isRequired())
 			return values;
 		else {
 			if (values == null) {
@@ -168,4 +217,18 @@
 		}
 	}
 
+	public boolean isPropertyResettable(final Object id) {
+		if (!(id instanceof AttributeDefinition))
+			return false;
+		return true;
+	}
+
+	private static class NamespaceUri {
+		public final String uri;
+
+		public NamespaceUri(final String uri) {
+			this.uri = uri;
+		}
+	}
+
 }
\ No newline at end of file