Author: veithen
Date: Wed Feb 25 23:47:46 2009
New Revision: 747970

URL: http://svn.apache.org/viewvc?rev=747970&view=rev
Log:
1. Consolidated the specification (Javadoc) of 
OMElement#addAttribute(OMAttribute) by defining a consistent set of 
requirements covering all possible cases and by accurately describing the side 
effect of addAttribute on namespace declarations. The specification is 
consistent with the current LLOM behavior, with two exceptions:
* If addAttribute replaces an existing attribute, it should obviously set the 
owner of the replaced attribute to null.
* If the attribute passed as argument is already owned by the element, 
addAttribute should be a no-op.

2. Added a set of test cases checking compliance with these requirements.

3. Fixed the addAttribute methods in OMElement (LLOM) and ElementImpl (DOOM) to 
conform to the requirements.

4. Fixed some other issues in DOOM that were preventing addAttribute from 
working properly.

Modified:
    
webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/om/OMElement.java
    
webservices/commons/trunk/modules/axiom/modules/axiom-api/src/test/java/org/apache/axiom/om/OMElementTestBase.java
    
webservices/commons/trunk/modules/axiom/modules/axiom-dom/src/main/java/org/apache/axiom/om/impl/dom/AttrImpl.java
    
webservices/commons/trunk/modules/axiom/modules/axiom-dom/src/main/java/org/apache/axiom/om/impl/dom/AttributeMap.java
    
webservices/commons/trunk/modules/axiom/modules/axiom-dom/src/main/java/org/apache/axiom/om/impl/dom/ElementImpl.java
    
webservices/commons/trunk/modules/axiom/modules/axiom-impl/src/main/java/org/apache/axiom/om/impl/llom/OMElementImpl.java

Modified: 
webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/om/OMElement.java
URL: 
http://svn.apache.org/viewvc/webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/om/OMElement.java?rev=747970&r1=747969&r2=747970&view=diff
==============================================================================
--- 
webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/om/OMElement.java
 (original)
+++ 
webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/om/OMElement.java
 Wed Feb 25 23:47:46 2009
@@ -156,11 +156,32 @@
 
     /**
      * Adds an attribute to this element.
-     * <p/>
-     * <p>There is no order implied by added attributes.</p>
+     * <p>
+     * If the attribute already has an owner, the attribute is cloned (i.e. 
its name, value and
+     * namespace are copied to a new attribute) and the new attribute is added 
to the element.
+     * Otherwise the existing instance specified by the <code>attr</code> 
parameter is added to
+     * the element. In both cases the owner of the added attribute is set to 
be the particular
+     * <code>OMElement</code>.
+     * <p>
+     * If there is already an attribute with the same name and namespace URI, 
it will be replaced
+     * and its owner set to <code>null</code>.
+     * <p>
+     * In the particular case where the attribute specified by 
<code>attr</code> is already owned
+     * by the element, calling this method has no effect. 
+     * <p>
+     * Attributes are not stored in any particular order. In particular, there 
is no guarantee
+     * that the added attribute will be returned as the last item by the 
iterator produced by
+     * a subsequent call to {...@link #getAllAttributes()}.
+     * <p>
+     * If the attribute being added has a namespace, but no corresponding 
namespace declaration is
+     * in scope for the element (i.e. declared on the element or one of its 
ancestors), a new
+     * namespace declaration is added to the element. Note that both the 
namespace prefix and URI
+     * are taken into account when looking for an existing namespace 
declaration.
      *
      * @param attr The attribute to add.
-     * @return Returns the passed in attribute.
+     * @return The attribute that was added to the element. As described above 
this may or may
+     *         not be the same as <code>attr</code>, depending on whether the 
attribute specified
+     *         by this parameter already has an owner or not.  
      */
     OMAttribute addAttribute(OMAttribute attr);
 

Modified: 
webservices/commons/trunk/modules/axiom/modules/axiom-api/src/test/java/org/apache/axiom/om/OMElementTestBase.java
URL: 
http://svn.apache.org/viewvc/webservices/commons/trunk/modules/axiom/modules/axiom-api/src/test/java/org/apache/axiom/om/OMElementTestBase.java?rev=747970&r1=747969&r2=747970&view=diff
==============================================================================
--- 
webservices/commons/trunk/modules/axiom/modules/axiom-api/src/test/java/org/apache/axiom/om/OMElementTestBase.java
 (original)
+++ 
webservices/commons/trunk/modules/axiom/modules/axiom-api/src/test/java/org/apache/axiom/om/OMElementTestBase.java
 Wed Feb 25 23:47:46 2009
@@ -20,6 +20,8 @@
 package org.apache.axiom.om;
 
 import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
 
 import javax.xml.namespace.QName;
 import javax.xml.stream.XMLStreamConstants;
@@ -138,4 +140,98 @@
         assertNotNull(ns);
         assertEquals("urn:a", ns.getNamespaceURI());
     }
+    
+    /**
+     * Test that calling {...@link OMElement#addAttribute(OMAttribute)} with 
an attribute that is
+     * already owned by another element will clone the attribute.
+     */
+    public void testAddAttributeAlreadyOwnedByOtherElement() {
+        OMFactory factory = getOMFactory();
+        OMElement element1 = factory.createOMElement(new QName("test"));
+        OMElement element2 = factory.createOMElement(new QName("test"));
+        OMAttribute att1 = element1.addAttribute("test", "test", null);
+        OMAttribute att2 = element2.addAttribute(att1);
+        assertSame(element1, att1.getOwner());
+        assertNotSame(att1, att2);
+        assertSame(element2, att2.getOwner());
+    }
+    
+    /**
+     * Test that calling {...@link OMElement#addAttribute(OMAttribute)} with 
an attribute that is
+     * already owned by the element is a no-op.
+     */
+    public void testAddAttributeAlreadyOwnedByElement() {
+        OMFactory factory = getOMFactory();
+        OMElement element = factory.createOMElement(new QName("test"));
+        OMAttribute att = element.addAttribute("test", "test", null);
+        OMAttribute result = element.addAttribute(att);
+        assertSame(result, att);
+        assertSame(element, att.getOwner());
+        Iterator it = element.getAllAttributes();
+        assertTrue(it.hasNext());
+        assertSame(att, it.next());
+        assertFalse(it.hasNext());
+    }
+    
+    /**
+     * Test that {...@link OMElement#addAttribute(OMAttribute)} behaves 
correctly when an attribute
+     * with the same name and namespace URI already exists.
+     */
+    public void testAddAttributeReplace() {
+        OMFactory factory = getOMFactory();
+        // Use same namespace URI but different prefixes
+        OMNamespace ns1 = factory.createOMNamespace("urn:ns", "p1");
+        OMNamespace ns2 = factory.createOMNamespace("urn:ns", "p2");
+        OMElement element = factory.createOMElement(new QName("test"));
+        OMAttribute att1 = factory.createOMAttribute("test", ns1, "test");
+        OMAttribute att2 = factory.createOMAttribute("test", ns2, "test");
+        element.addAttribute(att1);
+        element.addAttribute(att2);
+        Iterator it = element.getAllAttributes();
+        assertTrue(it.hasNext());
+        assertSame(att2, it.next());
+        assertFalse(it.hasNext());
+        assertNull(att1.getOwner());
+        assertSame(element, att2.getOwner());
+    }
+    
+    // This methods filters out the ("", "") namespace declaration (empty 
namespace
+    // to default). This declaration is present on OMElements produced by DOOM.
+    // TODO: check if this is not a bug in DOOM
+    private Iterator getRealAllDeclaredNamespaces(OMElement element) {
+        List namespaces = new LinkedList();
+        for (Iterator it = element.getAllDeclaredNamespaces(); it.hasNext(); ) 
{
+            OMNamespace ns = (OMNamespace)it.next();
+            if (!("".equals(ns.getPrefix()) && 
"".equals(ns.getNamespaceURI()))) {
+                namespaces.add(ns);
+            }
+        }
+        return namespaces.iterator();
+    }
+    
+    public void testAddAttributeWithoutExistingNamespaceDeclaration() {
+        OMFactory factory = getOMFactory();
+        OMElement element = factory.createOMElement(new QName("test"));
+        OMNamespace ns = factory.createOMNamespace("urn:ns", "p");
+        OMAttribute att = factory.createOMAttribute("test", ns, "test");
+        element.addAttribute(att);
+        assertEquals(ns, element.findNamespace(ns.getNamespaceURI(), 
ns.getPrefix()));
+        Iterator it = getRealAllDeclaredNamespaces(element);
+        assertTrue(it.hasNext());
+        assertEquals(ns, it.next());
+        assertFalse(it.hasNext());
+    }
+
+    public void testAddAttributeWithExistingNamespaceDeclaration() {
+        OMFactory factory = getOMFactory();
+        OMElement element = factory.createOMElement(new QName("test"));
+        OMNamespace ns = factory.createOMNamespace("urn:ns", "p");
+        element.declareNamespace(ns);
+        OMAttribute att = factory.createOMAttribute("test", ns, "test");
+        element.addAttribute(att);
+        Iterator it = getRealAllDeclaredNamespaces(element);
+        assertTrue(it.hasNext());
+        assertEquals(ns, it.next());
+        assertFalse(it.hasNext());
+    }
 }

Modified: 
webservices/commons/trunk/modules/axiom/modules/axiom-dom/src/main/java/org/apache/axiom/om/impl/dom/AttrImpl.java
URL: 
http://svn.apache.org/viewvc/webservices/commons/trunk/modules/axiom/modules/axiom-dom/src/main/java/org/apache/axiom/om/impl/dom/AttrImpl.java?rev=747970&r1=747969&r2=747970&view=diff
==============================================================================
--- 
webservices/commons/trunk/modules/axiom/modules/axiom-dom/src/main/java/org/apache/axiom/om/impl/dom/AttrImpl.java
 (original)
+++ 
webservices/commons/trunk/modules/axiom/modules/axiom-dom/src/main/java/org/apache/axiom/om/impl/dom/AttrImpl.java
 Wed Feb 25 23:47:46 2009
@@ -400,6 +400,8 @@
             }
         }
         clone.isSpecified(true);
+        clone.setParent(null);
+        clone.setUsed(false);
         return clone;
     }
 

Modified: 
webservices/commons/trunk/modules/axiom/modules/axiom-dom/src/main/java/org/apache/axiom/om/impl/dom/AttributeMap.java
URL: 
http://svn.apache.org/viewvc/webservices/commons/trunk/modules/axiom/modules/axiom-dom/src/main/java/org/apache/axiom/om/impl/dom/AttributeMap.java?rev=747970&r1=747969&r2=747970&view=diff
==============================================================================
--- 
webservices/commons/trunk/modules/axiom/modules/axiom-dom/src/main/java/org/apache/axiom/om/impl/dom/AttributeMap.java
 (original)
+++ 
webservices/commons/trunk/modules/axiom/modules/axiom-dom/src/main/java/org/apache/axiom/om/impl/dom/AttributeMap.java
 Wed Feb 25 23:47:46 2009
@@ -149,6 +149,7 @@
         }
         //Set the owner node
         attr.ownerNode = (DocumentImpl) this.ownerNode.getOwnerDocument();
+        attr.parent = this.ownerNode;
         attr.isOwned(true); // To indicate that this attr belong to an element
 
         int i = findNamePoint(attr.getNamespaceURI(), attr.getLocalName());
@@ -159,6 +160,7 @@
             nodes.setElementAt(attr, i);
             previous.ownerNode = (DocumentImpl) this.ownerNode
                     .getOwnerDocument();
+            previous.parent = null;
             previous.isOwned(false);
             // make sure it won't be mistaken with defaults in case it's reused
             previous.isSpecified(true);

Modified: 
webservices/commons/trunk/modules/axiom/modules/axiom-dom/src/main/java/org/apache/axiom/om/impl/dom/ElementImpl.java
URL: 
http://svn.apache.org/viewvc/webservices/commons/trunk/modules/axiom/modules/axiom-dom/src/main/java/org/apache/axiom/om/impl/dom/ElementImpl.java?rev=747970&r1=747969&r2=747970&view=diff
==============================================================================
--- 
webservices/commons/trunk/modules/axiom/modules/axiom-dom/src/main/java/org/apache/axiom/om/impl/dom/ElementImpl.java
 (original)
+++ 
webservices/commons/trunk/modules/axiom/modules/axiom-dom/src/main/java/org/apache/axiom/om/impl/dom/ElementImpl.java
 Wed Feb 25 23:47:46 2009
@@ -675,6 +675,16 @@
 
     /** @see org.apache.axiom.om.OMElement#addAttribute 
(org.apache.axiom.om.OMAttribute) */
     public OMAttribute addAttribute(OMAttribute attr) {
+        // If the attribute already has an owner element then clone the 
attribute (except if it is owned
+        // by the this element)
+        OMElement owner = attr.getOwner();
+        if (owner != null) {
+            if (owner == this) {
+                return attr;
+            }
+            attr = (OMAttribute)((AttrImpl)attr).cloneNode(false);
+        }
+        
         OMNamespace namespace = attr.getNamespace();
         if (namespace != null && namespace.getNamespaceURI() != null
                 && !"".equals(namespace.getNamespaceURI())
@@ -684,10 +694,11 @@
         }
 
         if (attr.getNamespace() != null) { // If the attr has a namespace
-            return (AttrImpl) this.setAttributeNode((Attr) attr);
+            this.setAttributeNodeNS((Attr) attr);
         } else {
-            return (AttrImpl) this.setAttributeNodeNS((Attr) attr);
+            this.setAttributeNode((Attr) attr);
         }
+        return attr;
     }
 
     /**

Modified: 
webservices/commons/trunk/modules/axiom/modules/axiom-impl/src/main/java/org/apache/axiom/om/impl/llom/OMElementImpl.java
URL: 
http://svn.apache.org/viewvc/webservices/commons/trunk/modules/axiom/modules/axiom-impl/src/main/java/org/apache/axiom/om/impl/llom/OMElementImpl.java?rev=747970&r1=747969&r2=747970&view=diff
==============================================================================
--- 
webservices/commons/trunk/modules/axiom/modules/axiom-impl/src/main/java/org/apache/axiom/om/impl/llom/OMElementImpl.java
 (original)
+++ 
webservices/commons/trunk/modules/axiom/modules/axiom-impl/src/main/java/org/apache/axiom/om/impl/llom/OMElementImpl.java
 Wed Feb 25 23:47:46 2009
@@ -574,8 +574,13 @@
      * @see OMAttributeImpl#equals(Object)
      */
     public OMAttribute addAttribute(OMAttribute attr){
-        // If the attribute already has an owner element then clone the 
attribute
-        if (attr.getOwner() !=null){
+        // If the attribute already has an owner element then clone the 
attribute (except if it is owned
+        // by the this element)
+        OMElement owner = attr.getOwner();
+        if (owner != null) {
+            if (owner == this) {
+                return attr;
+            }
             attr = new OMAttributeImpl(
                     attr.getLocalName(), attr.getNamespace(), 
attr.getAttributeValue(), attr.getOMFactory());
         }
@@ -594,7 +599,11 @@
 
         // Set the owner element of the attribute
         ((OMAttributeImpl)attr).owner = this;
-        attributes.put(attr.getQName(), attr);
+        OMAttributeImpl oldAttr = 
(OMAttributeImpl)attributes.put(attr.getQName(), attr);
+        // Did we replace an existing attribute?
+        if (oldAttr != null) {
+            oldAttr.owner = null;
+        }
         return attr;
     }
 


Reply via email to