bug 253753: dialog to edit the namespace declarations of the current
element
Signed-off-by: Florian Thienel <florian@thienel.org>
diff --git a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/dom/Document.java b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/dom/Document.java
index 3097698..4c49e2b 100644
--- a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/dom/Document.java
+++ b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/dom/Document.java
@@ -794,6 +794,10 @@
listeners.fireEvent("attributeChanged", e);
}
+ public void fireNamespaceChanged(final DocumentEvent e) {
+ listeners.fireEvent("namespaceChanged", e);
+ }
+
private void fireBeforeContentDeleted(final DocumentEvent e) {
listeners.fireEvent("beforeContentDeleted", e);
}
diff --git a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/dom/DocumentListener.java b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/dom/DocumentListener.java
index cc9314a..1e5092d 100644
--- a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/dom/DocumentListener.java
+++ b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/dom/DocumentListener.java
@@ -24,6 +24,14 @@
public void attributeChanged(DocumentEvent e);
/**
+ * Called when a namespace delcaration is changed in one of the document's elements.
+ *
+ * @param e
+ * the document event.
+ */
+ public void namespaceChanged(DocumentEvent e);
+
+ /**
* Called before content is deleted from a document.
*
* @param e
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 eb95d37..f5182bf 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
@@ -322,11 +322,29 @@
public void declareNamespace(final String namespacePrefix, final String namespaceURI) {
if (namespaceURI == null || "".equals(namespaceURI.trim()))
return;
- namespaceDeclarations.put(namespacePrefix, namespaceURI);
+ final String oldNamespaceURI = namespaceDeclarations.put(namespacePrefix, namespaceURI);
+ final Document document = getDocument();
+ if (document == null) // doc may be null, e.g. when we're cloning an element to produce a document fragment
+ return;
+
+ if (namespaceURI.equals(oldNamespaceURI)) // if we do not change anything, we do not fire anything
+ return;
+
+ final IUndoableEdit edit = document.isUndoEnabled() ? new NamespaceChangeEdit(namespacePrefix, oldNamespaceURI, namespaceURI) : null;
+ document.fireNamespaceChanged(new DocumentEvent(document, this, getStartOffset(), 0, edit));
}
public void removeNamespace(String namespacePrefix) {
- namespaceDeclarations.remove(namespacePrefix);
+ final String oldNamespaceURI = namespaceDeclarations.remove(namespacePrefix);
+ final Document document = getDocument();
+ if (document == null) // doc may be null, e.g. when we're cloning an element to produce a document fragment
+ return;
+
+ if (oldNamespaceURI == null)
+ return; // we have actually removed nothing, so we should not tell anybody about it
+
+ final IUndoableEdit edit = document.isUndoEnabled() ? new NamespaceChangeEdit(namespacePrefix, oldNamespaceURI, null) : null;
+ document.fireNamespaceChanged(new DocumentEvent(document, this, getStartOffset(), 0, edit));
}
public void declareDefaultNamespace(final String namespaceURI) {
@@ -360,11 +378,11 @@
private class AttributeChangeEdit implements IUndoableEdit {
- private QualifiedName name;
- private String oldValue;
- private String newValue;
+ private final QualifiedName name;
+ private final String oldValue;
+ private final String newValue;
- public AttributeChangeEdit(QualifiedName name, String oldValue, String newValue) {
+ public AttributeChangeEdit(final QualifiedName name, final String oldValue, final String newValue) {
this.name = name;
this.oldValue = oldValue;
this.newValue = newValue;
@@ -375,7 +393,7 @@
}
public void undo() throws CannotUndoException {
- Document doc = (Document) getDocument();
+ final Document doc = (Document) getDocument();
try {
doc.setUndoEnabled(false);
setAttribute(name, oldValue);
@@ -387,7 +405,7 @@
}
public void redo() throws CannotRedoException {
- Document doc = (Document) getDocument();
+ final Document doc = (Document) getDocument();
try {
doc.setUndoEnabled(false);
setAttribute(name, newValue);
@@ -398,5 +416,52 @@
}
}
}
+
+ private class NamespaceChangeEdit implements IUndoableEdit {
+
+ private final String prefix;
+ private final String oldUri;
+ private final String newUri;
+
+ public NamespaceChangeEdit(final String prefix, final String oldUri, final String newUri) {
+ this.prefix = prefix;
+ this.oldUri = oldUri;
+ this.newUri = newUri;
+ }
+
+ public boolean combine(IUndoableEdit edit) {
+ return false;
+ }
+
+ public void undo() throws CannotUndoException {
+ final Document doc = (Document) getDocument();
+ try {
+ doc.setUndoEnabled(false);
+ if (oldUri == null)
+ removeNamespace(prefix);
+ else
+ declareNamespace(prefix, oldUri);
+ } catch (DocumentValidationException ex) {
+ throw new CannotUndoException();
+ } finally {
+ doc.setUndoEnabled(true);
+ }
+ }
+
+ public void redo() throws CannotRedoException {
+ final Document doc = (Document) getDocument();
+ try {
+ doc.setUndoEnabled(false);
+ if (newUri == null)
+ removeNamespace(prefix);
+ else
+ declareNamespace(prefix, newUri);
+ } catch (DocumentValidationException ex) {
+ throw new CannotUndoException();
+ } finally {
+ doc.setUndoEnabled(true);
+ }
+ }
+ }
}
diff --git a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/widget/VexWidgetImpl.java b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/widget/VexWidgetImpl.java
index 04ea3c9..233a947 100644
--- a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/widget/VexWidgetImpl.java
+++ b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/widget/VexWidgetImpl.java
@@ -160,6 +160,16 @@
addEdit(e.getUndoableEdit(), getCaretOffset());
}
+ public void namespaceChanged(DocumentEvent e) {
+ invalidateElementBox(e.getParentElement());
+
+ if (beginWorkCount == 0)
+ VexWidgetImpl.this.relayout();
+
+ addEdit(e.getUndoableEdit(), getCaretOffset());
+ hostComponent.fireSelectionChanged();
+ }
+
};
/**
diff --git a/org.eclipse.vex.ui.tests/META-INF/MANIFEST.MF b/org.eclipse.vex.ui.tests/META-INF/MANIFEST.MF
index f4f8436..570874e 100644
--- a/org.eclipse.vex.ui.tests/META-INF/MANIFEST.MF
+++ b/org.eclipse.vex.ui.tests/META-INF/MANIFEST.MF
@@ -7,6 +7,7 @@
Require-Bundle: org.eclipse.ui,
org.eclipse.core.runtime,
org.eclipse.core.resources,
+ org.eclipse.vex.core;bundle-version="[1.0.0,2.0.0)",
org.eclipse.vex.ui;bundle-version="[1.0.0,2.0.0)",
org.junit;bundle-version="3.8.1",
org.eclipse.jface.text;bundle-version="3.0.0"
diff --git a/org.eclipse.vex.ui.tests/src/org/eclipse/vex/ui/internal/namespace/tests/EditNamespacesControllerTest.java b/org.eclipse.vex.ui.tests/src/org/eclipse/vex/ui/internal/namespace/tests/EditNamespacesControllerTest.java
new file mode 100644
index 0000000..ddc6775
--- /dev/null
+++ b/org.eclipse.vex.ui.tests/src/org/eclipse/vex/ui/internal/namespace/tests/EditNamespacesControllerTest.java
@@ -0,0 +1,157 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Florian Thienel 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:
+ * Florian Thienel - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.vex.ui.internal.namespace.tests;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.util.List;
+
+import org.eclipse.core.runtime.QualifiedName;
+import org.eclipse.vex.core.internal.dom.Element;
+import org.eclipse.vex.ui.internal.namespace.EditNamespacesController;
+import org.eclipse.vex.ui.internal.namespace.EditableNamespaceDefinition;
+import org.junit.Test;
+
+/**
+ * @author Florian Thienel
+ */
+public class EditNamespacesControllerTest {
+
+ @Test
+ public void populateFromElement() throws Exception {
+ final Element element = new Element(new QualifiedName("http://namespace/uri/default", "element"));
+ element.declareDefaultNamespace("http://namespace/uri/default");
+ element.declareNamespace("ns1", "http://namespace/uri/1");
+ element.declareNamespace("ns2", "http://namespace/uri/2");
+ final EditNamespacesController controller = new EditNamespacesController(element);
+
+ assertEquals("http://namespace/uri/default", controller.getDefaultNamespaceURI());
+
+ final List<EditableNamespaceDefinition> namespaces = controller.getNamespaceDefinitions();
+ assertEquals(2, namespaces.size());
+ assertContainsNamespaceDefinition(new EditableNamespaceDefinition("ns1", "http://namespace/uri/1"), namespaces);
+ assertContainsNamespaceDefinition(new EditableNamespaceDefinition("ns2", "http://namespace/uri/2"), namespaces);
+ }
+
+ @Test
+ public void defaultNamespaceNotNull() throws Exception {
+ final EditNamespacesController controller = new EditNamespacesController(new Element("element"));
+ assertNotNull(controller.getDefaultNamespaceURI());
+ }
+
+ @Test
+ public void editDefaultNamespace() throws Exception {
+ final Element element = new Element("element");
+ final EditNamespacesController controller = new EditNamespacesController(element);
+ controller.setDefaultNamespaceURI("http://namespace/uri/default");
+ assertEquals("http://namespace/uri/default", controller.getDefaultNamespaceURI());
+ assertNull(element.getDeclaredDefaultNamespaceURI());
+
+ controller.applyToElement();
+ assertEquals("http://namespace/uri/default", element.getDefaultNamespaceURI());
+ assertNull(element.getQualifiedName().getQualifier());
+ }
+
+ @Test
+ public void removeDefaultNamespace() throws Exception {
+ final Element element = new Element(new QualifiedName("http://namespace/uri/default", "element"));
+ element.declareDefaultNamespace("http://namespace/uri/default");
+ final EditNamespacesController controller = new EditNamespacesController(element);
+
+ controller.setDefaultNamespaceURI("");
+ controller.applyToElement();
+ assertNull(element.getDeclaredDefaultNamespaceURI());
+ assertEquals("http://namespace/uri/default", element.getQualifiedName().getQualifier());
+ }
+
+ @Test
+ public void addNamespaceDefinition() throws Exception {
+ final Element element = new Element("element");
+ final EditNamespacesController controller = new EditNamespacesController(element);
+
+ assertTrue(controller.getNamespaceDefinitions().isEmpty());
+ assertTrue(element.getDeclaredNamespacePrefixes().isEmpty());
+
+ final EditableNamespaceDefinition newDefinition = controller.addNamespaceDefinition();
+ assertNotNull(newDefinition);
+
+ assertEquals(1, controller.getNamespaceDefinitions().size());
+ assertSame(newDefinition, controller.getNamespaceDefinitions().get(0));
+ assertTrue(element.getDeclaredNamespacePrefixes().isEmpty());
+
+ newDefinition.setPrefix("ns1");
+ newDefinition.setUri("http://namespace/uri/1");
+
+ controller.applyToElement();
+ assertEquals(1, element.getDeclaredNamespacePrefixes().size());
+ }
+
+ @Test
+ public void removeNamespaceDefinition() throws Exception {
+ final Element element = new Element("element");
+ element.declareNamespace("ns1", "http://namespace/uri/1");
+ final EditNamespacesController controller = new EditNamespacesController(element);
+
+ controller.removeNamespaceDefinition(controller.getNamespaceDefinitions().get(0));
+
+ assertTrue(controller.getNamespaceDefinitions().isEmpty());
+ assertTrue(element.getDeclaredNamespacePrefixes().contains("ns1"));
+
+ controller.applyToElement();
+ assertTrue(element.getDeclaredNamespacePrefixes().isEmpty());
+ }
+
+ @Test
+ public void editNamespacePrefix() throws Exception {
+ final Element element = new Element("element");
+ element.declareNamespace("ns1", "http://namespace/uri/1");
+ final EditNamespacesController controller = new EditNamespacesController(element);
+ final EditableNamespaceDefinition definition = controller.getNamespaceDefinitions().get(0);
+
+ definition.setPrefix("ns2");
+ assertTrue(element.getDeclaredNamespacePrefixes().contains("ns1"));
+ assertEquals(1, element.getDeclaredNamespacePrefixes().size());
+
+ controller.applyToElement();
+ assertFalse(element.getDeclaredNamespacePrefixes().contains("ns1"));
+ assertTrue(element.getDeclaredNamespacePrefixes().contains("ns2"));
+ assertEquals(1, element.getDeclaredNamespacePrefixes().size());
+ }
+
+ @Test
+ public void editNamespaceUri() throws Exception {
+ final Element element = new Element("element");
+ element.declareNamespace("ns1", "http://namespace/uri/1");
+ final EditNamespacesController controller = new EditNamespacesController(element);
+ final EditableNamespaceDefinition definition = controller.getNamespaceDefinitions().get(0);
+
+ definition.setUri("http://namespace/uri/2");
+ assertEquals("http://namespace/uri/1", element.getNamespaceURI("ns1"));
+
+ controller.applyToElement();
+ assertEquals("http://namespace/uri/2", element.getNamespaceURI("ns1"));
+ assertEquals(1, element.getDeclaredNamespacePrefixes().size());
+ }
+
+ private static void assertContainsNamespaceDefinition(final EditableNamespaceDefinition expected, final List<EditableNamespaceDefinition> actualList) {
+ for (final EditableNamespaceDefinition definition : actualList)
+ if (expected.getPrefix().equals(definition.getPrefix()) && expected.getUri().equals(definition.getUri()))
+ return;
+ fail("namespace definition not found: " + expected.getPrefix() + "=" + expected.getUri());
+ }
+
+}
diff --git a/org.eclipse.vex.ui.tests/src/org/eclipse/vex/ui/tests/VexUiTestSuite.java b/org.eclipse.vex.ui.tests/src/org/eclipse/vex/ui/tests/VexUiTestSuite.java
index ac10c14..49088a8 100644
--- a/org.eclipse.vex.ui.tests/src/org/eclipse/vex/ui/tests/VexUiTestSuite.java
+++ b/org.eclipse.vex.ui.tests/src/org/eclipse/vex/ui/tests/VexUiTestSuite.java
@@ -17,6 +17,7 @@
import org.eclipse.vex.ui.internal.config.tests.ConfigLoaderJobTest;
import org.eclipse.vex.ui.internal.config.tests.ConfigurationRegistryTest;
import org.eclipse.vex.ui.internal.editor.tests.FindReplaceTargetTest;
+import org.eclipse.vex.ui.internal.namespace.tests.EditNamespacesControllerTest;
import org.eclipse.vex.ui.internal.tests.ResourceTrackerTest;
public class VexUiTestSuite extends TestSuite {
@@ -29,6 +30,7 @@
super("Vex UI Tests"); //$NON-NLS-1$
addTest(new JUnit4TestAdapter(ConfigLoaderJobTest.class));
addTest(new JUnit4TestAdapter(ConfigurationRegistryTest.class));
+ addTest(new JUnit4TestAdapter(EditNamespacesControllerTest.class));
addTestSuite(IconTest.class);
addTestSuite(FindReplaceTargetTest.class);
addTestSuite(ResourceTrackerTest.class);
diff --git a/org.eclipse.vex.ui/META-INF/MANIFEST.MF b/org.eclipse.vex.ui/META-INF/MANIFEST.MF
index 8d500da..0513aaf 100644
--- a/org.eclipse.vex.ui/META-INF/MANIFEST.MF
+++ b/org.eclipse.vex.ui/META-INF/MANIFEST.MF
@@ -25,6 +25,7 @@
org.eclipse.vex.ui.internal.config;x-internal:=true,
org.eclipse.vex.ui.internal.editor;x-friends:="org.eclipse.vex.docbook",
org.eclipse.vex.ui.internal.handlers;x-internal:=true,
+ org.eclipse.vex.ui.internal.namespace,
org.eclipse.vex.ui.internal.outline;x-friends:="org.eclipse.vex.docbook",
org.eclipse.vex.ui.internal.perspective;x-internal:=true,
org.eclipse.vex.ui.internal.property;x-internal:=true,
diff --git a/org.eclipse.vex.ui/plugin.properties b/org.eclipse.vex.ui/plugin.properties
index 6ac359b..543c8c3 100644
--- a/org.eclipse.vex.ui/plugin.properties
+++ b/org.eclipse.vex.ui/plugin.properties
@@ -12,17 +12,17 @@
pluginName= Vex UI
providerName= Eclipse.org
-BuildProblemDecorator.name=Visual XML Plug-in Build Problem Decorator
+BuildProblemDecorator.name=Visual XML Plug-in Build Problem
ConfigurationView.name=Visual XML Configuration
DebugView.name=Visual XML Debug
-DocumentPerspective.name=Document
+DocumentPerspective.name=XML Authoring
NewDocumentWizard.name=Document
NewDocumentWizard.desc=Create an XML document using the Visual Editor for XML.
NewPluginProjectWizard.name=Visual XML Plug-in Project
NewPluginProjectWizard.desc=Create a Visual XML Plug-in project to register XML types and to associate styles to edit these types of files with the Visual XML Editor.
NewWizardCategory.name=XML Authoring
PluginProjectBuilder.name=Visual XML Plug-in Builder
-PluginProjectDecorator.name=Visual XML Plug-in Project Decorator
+PluginProjectDecorator.name=Visual XML Plug-in Project
PluginProjectNature.name=Visual XML Plug-in Project Nature
DoctypePropertyPage.name=Visual XML Document Type
StylePropertyPage.name=Visual XML Style
@@ -57,6 +57,7 @@
command.nextTableCell.name= Next Table Cell
command.splitBlockElement.name= Split Block Element
command.splitItem.name= Split Item
+command.editNamespaces.name= Edit Namespaces...
menu.Document.name= &Document
menu.Add.name= &Add
@@ -86,4 +87,5 @@
menu.Column.MoveRight.name= Move Right
menu.Column.Remove.name= Remove
menu.Style.name= Style
+
contentType.XmlDocument.name=XML Document
diff --git a/org.eclipse.vex.ui/plugin.xml b/org.eclipse.vex.ui/plugin.xml
index 2bb4fba..7e440c5 100644
--- a/org.eclipse.vex.ui/plugin.xml
+++ b/org.eclipse.vex.ui/plugin.xml
@@ -79,6 +79,11 @@
</command>
<command
categoryId="org.eclipse.vex.ui.commands.category"
+ id="org.eclipse.vex.ui.EditNamespacesCommand"
+ name="%command.editNamespaces.name">
+ </command>
+ <command
+ categoryId="org.eclipse.vex.ui.commands.category"
id="org.eclipse.vex.ui.DuplicateSelectionCommand"
name="%command.duplicateSelection.name">
</command>
@@ -229,6 +234,15 @@
</activeWhen>
</handler>
<handler
+ class="org.eclipse.vex.ui.internal.namespace.EditNamespacesHandler"
+ commandId="org.eclipse.vex.ui.EditNamespacesCommand">
+ <activeWhen>
+ <reference
+ definitionId="org.eclipse.vex.ui.activeVexEditor">
+ </reference>
+ </activeWhen>
+ </handler>
+ <handler
class="org.eclipse.vex.ui.internal.handlers.DuplicateSelectionHandler"
commandId="org.eclipse.vex.ui.DuplicateSelectionCommand">
<activeWhen>
@@ -667,6 +681,9 @@
<command
commandId="org.eclipse.vex.ui.ConvertElementCommand">
</command>
+ <command
+ commandId="org.eclipse.vex.ui.EditNamespacesCommand">
+ </command>
<menu
label="%menu.Move.name">
<command
@@ -745,6 +762,9 @@
<command
commandId="org.eclipse.vex.ui.RemoveTagCommand">
</command>
+ <command
+ commandId="org.eclipse.vex.ui.EditNamespacesCommand">
+ </command>
<separator
name="org.eclipse.vex.ui.popup.vexTableItems"
visible="true">
diff --git a/org.eclipse.vex.ui/src/org/eclipse/vex/ui/internal/namespace/EditNamespacesController.java b/org.eclipse.vex.ui/src/org/eclipse/vex/ui/internal/namespace/EditNamespacesController.java
new file mode 100644
index 0000000..31b216a
--- /dev/null
+++ b/org.eclipse.vex.ui/src/org/eclipse/vex/ui/internal/namespace/EditNamespacesController.java
@@ -0,0 +1,89 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Florian Thienel 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:
+ * Florian Thienel - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.vex.ui.internal.namespace;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+
+import org.eclipse.vex.core.internal.dom.Element;
+
+/**
+ * @author Florian Thienel
+ */
+public class EditNamespacesController {
+
+ private final Element element;
+
+ private String defaultNamespaceURI;
+
+ private final List<EditableNamespaceDefinition> namespaceDefinitions;
+
+ public EditNamespacesController(final Element element) {
+ this.element = element;
+ defaultNamespaceURI = getDefaultNamespaceURI(element);
+ namespaceDefinitions = getNamespaceDefinitions(element);
+ }
+
+ private static String getDefaultNamespaceURI(final Element element) {
+ final String result = element.getDeclaredDefaultNamespaceURI();
+ if (result == null)
+ return "";
+ return result;
+ }
+
+ private static List<EditableNamespaceDefinition> getNamespaceDefinitions(final Element element) {
+ final ArrayList<EditableNamespaceDefinition> result = new ArrayList<EditableNamespaceDefinition>();
+ for (final String prefix : element.getDeclaredNamespacePrefixes())
+ result.add(new EditableNamespaceDefinition(prefix, element.getNamespaceURI(prefix)));
+ return result;
+ }
+
+ public String getDefaultNamespaceURI() {
+ return defaultNamespaceURI;
+ }
+
+ public void setDefaultNamespaceURI(final String defaultNamespaceURI) {
+ this.defaultNamespaceURI = defaultNamespaceURI;
+ }
+
+ public List<EditableNamespaceDefinition> getNamespaceDefinitions() {
+ return namespaceDefinitions;
+ }
+
+ public EditableNamespaceDefinition addNamespaceDefinition() {
+ final EditableNamespaceDefinition result = new EditableNamespaceDefinition();
+ namespaceDefinitions.add(result);
+ return result;
+ }
+
+ public void removeNamespaceDefinition(EditableNamespaceDefinition namespaceDefinition) {
+ namespaceDefinitions.remove(namespaceDefinition);
+ }
+
+ public void applyToElement() {
+ if (defaultNamespaceURI == null || "".equals(defaultNamespaceURI))
+ element.removeDefaultNamespace();
+ else
+ element.declareDefaultNamespace(defaultNamespaceURI);
+
+ final HashSet<String> declaredPrefixes = new HashSet<String>();
+ for (final EditableNamespaceDefinition definition : namespaceDefinitions) {
+ element.declareNamespace(definition.getPrefix(), definition.getUri());
+ declaredPrefixes.add(definition.getPrefix());
+ }
+
+ for (final String prefix : element.getDeclaredNamespacePrefixes())
+ if (!declaredPrefixes.contains(prefix))
+ element.removeNamespace(prefix);
+ }
+
+}
diff --git a/org.eclipse.vex.ui/src/org/eclipse/vex/ui/internal/namespace/EditNamespacesDialog.java b/org.eclipse.vex.ui/src/org/eclipse/vex/ui/internal/namespace/EditNamespacesDialog.java
new file mode 100644
index 0000000..c69f05f
--- /dev/null
+++ b/org.eclipse.vex.ui/src/org/eclipse/vex/ui/internal/namespace/EditNamespacesDialog.java
@@ -0,0 +1,236 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Florian Thienel 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:
+ * Florian Thienel - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.vex.ui.internal.namespace;
+
+import org.eclipse.jface.dialogs.TitleAreaDialog;
+import org.eclipse.jface.layout.TableColumnLayout;
+import org.eclipse.jface.viewers.ArrayContentProvider;
+import org.eclipse.jface.viewers.CellEditor;
+import org.eclipse.jface.viewers.CellLabelProvider;
+import org.eclipse.jface.viewers.ColumnWeightData;
+import org.eclipse.jface.viewers.EditingSupport;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.viewers.TableViewerColumn;
+import org.eclipse.jface.viewers.TextCellEditor;
+import org.eclipse.jface.viewers.ViewerCell;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.graphics.Point;
+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.Shell;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.vex.core.internal.dom.Element;
+
+/**
+ * @author Florian Thienel
+ */
+public class EditNamespacesDialog extends TitleAreaDialog {
+
+ private final EditNamespacesController controller;
+ private Text defaultNamespaceText;
+ private TableViewer namespacesTable;
+
+ public EditNamespacesDialog(final Shell parentShell, final Element element) {
+ super(parentShell);
+ this.controller = new EditNamespacesController(element);
+ }
+
+ @Override
+ protected void configureShell(final Shell newShell) {
+ super.configureShell(newShell);
+ newShell.setText("Edit Namespaces");
+ }
+
+ @Override
+ protected int getShellStyle() {
+ return super.getShellStyle() | SWT.RESIZE;
+ }
+
+ @Override
+ protected Point getInitialSize() {
+ final Point shellSize = super.getInitialSize();
+ return new Point(shellSize.x, Math.max(convertVerticalDLUsToPixels(200), shellSize.y));
+ }
+
+ @Override
+ protected Control createContents(final Composite parent) {
+ final Composite result = (Composite) super.createContents(parent);
+ setTitle("Edit Namespaces");
+ setMessage("Edit the namespaces of the selected element.");
+ return result;
+ }
+
+ @Override
+ protected Control createDialogArea(final Composite parent) {
+ final Composite superRoot = (Composite) super.createDialogArea(parent);
+ final Composite root = new Composite(superRoot, SWT.NONE);
+ root.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+ root.setLayout(new GridLayout(2, false));
+
+ final Label defaultNamespaceLabel = new Label(root, SWT.NONE);
+ defaultNamespaceLabel.setText("Default Namespace");
+ defaultNamespaceLabel.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 2, 1));
+
+ defaultNamespaceText = new Text(root, SWT.SINGLE | SWT.BORDER);
+ defaultNamespaceText.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 1, 1));
+ defaultNamespaceText.addModifyListener(new ModifyListener() {
+ public void modifyText(final ModifyEvent e) {
+ controller.setDefaultNamespaceURI(defaultNamespaceText.getText());
+ }
+ });
+
+ new Label(root, SWT.NONE); // just a placeholder
+
+ final Label namespaceTableLabel = new Label(root, SWT.NONE);
+ namespaceTableLabel.setText("Other Namespaces");
+ namespaceTableLabel.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 2, 1));
+
+ final Composite tableRoot = new Composite(root, SWT.NONE);
+ tableRoot.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 3));
+ final TableColumnLayout tableColumnLayout = new TableColumnLayout();
+ tableRoot.setLayout(tableColumnLayout);
+
+ namespacesTable = new TableViewer(tableRoot, SWT.BORDER | SWT.FULL_SELECTION);
+ namespacesTable.getTable().setHeaderVisible(true);
+ namespacesTable.getTable().setLinesVisible(true);
+ namespacesTable.setContentProvider(ArrayContentProvider.getInstance());
+
+ final TableViewerColumn prefixColumn = new TableViewerColumn(namespacesTable, SWT.NONE);
+ prefixColumn.getColumn().setText("Prefix");
+ prefixColumn.setLabelProvider(new CellLabelProvider() {
+ @Override
+ public void update(final ViewerCell cell) {
+ cell.setText(((EditableNamespaceDefinition) cell.getElement()).getPrefix());
+ }
+ });
+ prefixColumn.setEditingSupport(new EditingSupport(namespacesTable) {
+ @Override
+ protected CellEditor getCellEditor(final Object element) {
+ return new TextCellEditor(namespacesTable.getTable());
+ }
+
+ @Override
+ protected boolean canEdit(final Object element) {
+ return true;
+ }
+
+ @Override
+ protected Object getValue(final Object element) {
+ return ((EditableNamespaceDefinition) element).getPrefix();
+ }
+
+ @Override
+ protected void setValue(final Object element, final Object value) {
+ if (value != null)
+ ((EditableNamespaceDefinition) element).setPrefix(value.toString());
+ else
+ ((EditableNamespaceDefinition) element).setPrefix("");
+ namespacesTable.refresh(element);
+ }
+ });
+
+ final TableViewerColumn uriColumn = new TableViewerColumn(namespacesTable, SWT.NONE);
+ uriColumn.getColumn().setText("URI");
+ uriColumn.setLabelProvider(new CellLabelProvider() {
+ @Override
+ public void update(final ViewerCell cell) {
+ cell.setText(((EditableNamespaceDefinition) cell.getElement()).getUri());
+ }
+ });
+ uriColumn.setEditingSupport(new EditingSupport(namespacesTable) {
+ @Override
+ protected CellEditor getCellEditor(final Object element) {
+ return new TextCellEditor(namespacesTable.getTable());
+ }
+
+ @Override
+ protected boolean canEdit(final Object element) {
+ return true;
+ }
+
+ @Override
+ protected Object getValue(final Object element) {
+ return ((EditableNamespaceDefinition) element).getUri();
+ }
+
+ @Override
+ protected void setValue(final Object element, final Object value) {
+ if (value != null)
+ ((EditableNamespaceDefinition) element).setUri(value.toString());
+ else
+ ((EditableNamespaceDefinition) element).setUri("");
+ namespacesTable.refresh(element);
+ }
+ });
+
+ tableColumnLayout.setColumnData(prefixColumn.getColumn(), new ColumnWeightData(1, 100, true));
+ tableColumnLayout.setColumnData(uriColumn.getColumn(), new ColumnWeightData(3, 100, true));
+
+ final Button addNamespaceButton = new Button(root, SWT.PUSH);
+ addNamespaceButton.setText("Add");
+ addNamespaceButton.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
+ addNamespaceButton.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(final SelectionEvent e) {
+ addNamespacePressed();
+ }
+ });
+
+ final Button removeNamespaceButton = new Button(root, SWT.PUSH);
+ removeNamespaceButton.setText("Remove");
+ removeNamespaceButton.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
+ removeNamespaceButton.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(final SelectionEvent e) {
+ removeNamespacePressed();
+ }
+ });
+
+ populateFromController();
+
+ return superRoot;
+ }
+
+ private void populateFromController() {
+ defaultNamespaceText.setText(controller.getDefaultNamespaceURI());
+ namespacesTable.setInput(controller.getNamespaceDefinitions());
+ }
+
+ private void addNamespacePressed() {
+ final EditableNamespaceDefinition newDefinition = controller.addNamespaceDefinition();
+ namespacesTable.refresh();
+ namespacesTable.setSelection(new StructuredSelection(newDefinition), true);
+ }
+
+ private void removeNamespacePressed() {
+ final IStructuredSelection selection = (IStructuredSelection) namespacesTable.getSelection();
+ if (selection.isEmpty())
+ return;
+ final EditableNamespaceDefinition selectedDefinition = (EditableNamespaceDefinition) selection.getFirstElement();
+ controller.removeNamespaceDefinition(selectedDefinition);
+ namespacesTable.refresh();
+ }
+
+ protected void okPressed() {
+ controller.applyToElement();
+ super.okPressed();
+ };
+}
diff --git a/org.eclipse.vex.ui/src/org/eclipse/vex/ui/internal/namespace/EditNamespacesHandler.java b/org.eclipse.vex.ui/src/org/eclipse/vex/ui/internal/namespace/EditNamespacesHandler.java
new file mode 100644
index 0000000..f5b6962
--- /dev/null
+++ b/org.eclipse.vex.ui/src/org/eclipse/vex/ui/internal/namespace/EditNamespacesHandler.java
@@ -0,0 +1,31 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Florian Thienel 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:
+ * Florian Thienel - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.vex.ui.internal.namespace;
+
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.jface.window.Window;
+import org.eclipse.vex.ui.internal.handlers.AbstractVexWidgetHandler;
+import org.eclipse.vex.ui.internal.swt.VexWidget;
+
+/**
+ * @author Florian Thienel
+ */
+public class EditNamespacesHandler extends AbstractVexWidgetHandler {
+
+ @Override
+ public void execute(final VexWidget widget) throws ExecutionException {
+ final EditNamespacesDialog dialog = new EditNamespacesDialog(widget.getShell(), widget.getCurrentElement());
+ if (dialog.open() == Window.OK) {
+ ; // TODO maybe we have to refresh something in the widget...
+ }
+ }
+
+}
diff --git a/org.eclipse.vex.ui/src/org/eclipse/vex/ui/internal/namespace/EditableNamespaceDefinition.java b/org.eclipse.vex.ui/src/org/eclipse/vex/ui/internal/namespace/EditableNamespaceDefinition.java
new file mode 100644
index 0000000..fe072c0
--- /dev/null
+++ b/org.eclipse.vex.ui/src/org/eclipse/vex/ui/internal/namespace/EditableNamespaceDefinition.java
@@ -0,0 +1,47 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Florian Thienel 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:
+ * Florian Thienel - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.vex.ui.internal.namespace;
+
+/**
+ * @author Florian Thienel
+ */
+public class EditableNamespaceDefinition {
+
+ private String prefix;
+
+ private String uri;
+
+ public EditableNamespaceDefinition() {
+ this("", "");
+ }
+
+ public EditableNamespaceDefinition(final String prefix, final String uri) {
+ this.prefix = prefix;
+ this.uri = uri;
+ }
+
+ public String getPrefix() {
+ return prefix;
+ }
+
+ public void setPrefix(String prefix) {
+ this.prefix = prefix;
+ }
+
+ public String getUri() {
+ return uri;
+ }
+
+ public void setUri(String uri) {
+ this.uri = uri;
+ }
+
+}