package com.github.simy4.xpath.dom4j.navigator.node;

import org.dom4j.Attribute;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.Namespace;

import javax.annotation.Nonnull;
import javax.annotation.concurrent.Immutable;
import javax.xml.namespace.QName;
import java.util.Iterator;

@Immutable
public final class Dom4jElement implements Dom4jNode<Element> {

    private final Element element;

    public Dom4jElement(Element element) {
        this.element = element;
    }

    @Override
    public Element getNode() {
        return element;
    }

    @Override
    public QName getName() {
        final Namespace namespace = element.getNamespace();
        return new QName(namespace.getURI(), element.getName(), namespace.getPrefix());
    }

    @Override
    public String getText() {
        return element.getText();
    }

    @Override
    public Iterable<Dom4jNode<Element>> elements() {
        return new Iterable<Dom4jNode<Element>>() {
            @Override
            @Nonnull
            public Iterator<Dom4jNode<Element>> iterator() {
                return new Dom4jElementsIterator(element.elementIterator());
            }
        };
    }

    @Override
    public Iterable<Dom4jNode<Attribute>> attributes() {
        return new Iterable<Dom4jNode<Attribute>>() {
            @Override
            @Nonnull
            public Iterator<Dom4jNode<Attribute>> iterator() {
                return new Dom4jAttributesIterator(element.attributeIterator());
            }
        };
    }

    @Override
    public Dom4jNode<Attribute> createAttribute(org.dom4j.QName attribute) {
        final Attribute attr = DocumentHelper.createAttribute(element, attribute, "");
        element.attributes().add(attr);
        return new Dom4jAttribute(attr);
    }

    @Override
    public Dom4jNode<Element> createElement(org.dom4j.QName element) {
        return new Dom4jElement(this.element.addElement(element));
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }

        Dom4jElement that = (Dom4jElement) o;

        return element.equals(that.element);
    }

    @Override
    public int hashCode() {
        return element.hashCode();
    }

    @Override
    public String toString() {
        return element.toString();
    }

    private static final class Dom4jAttributesIterator implements Iterator<Dom4jNode<Attribute>> {

        private final Iterator<Attribute> elementIterator;

        Dom4jAttributesIterator(Iterator<Attribute> elementIterator) {
            this.elementIterator = elementIterator;
        }

        @Override
        public boolean hasNext() {
            return elementIterator.hasNext();
        }

        @Override
        public Dom4jNode<Attribute> next() {
            return new Dom4jAttribute(elementIterator.next());
        }

        @Override
        public void remove() {
            elementIterator.remove();
        }

    }

    private static final class Dom4jElementsIterator implements Iterator<Dom4jNode<Element>> {

        private final Iterator<Element> elementIterator;

        Dom4jElementsIterator(Iterator<Element> elementIterator) {
            this.elementIterator = elementIterator;
        }

        @Override
        public boolean hasNext() {
            return elementIterator.hasNext();
        }

        @Override
        public Dom4jNode<Element> next() {
            return new Dom4jElement(elementIterator.next());
        }

        @Override
        public void remove() {
            elementIterator.remove();
        }

    }

}

@@ -43,24 +43,126 @@
 ator.node;%0A%0A
+import com.github.simy4.xpath.util.Function;%0Aimport com.github.simy4.xpath.util.TransformingIterator;%0A
 import org.d
@@ -1213,52 +1213,131 @@
 new 
-Dom4jElementsIterator(element.elementIterato
+TransformingIterator%3CElement, Dom4jNode%3CElement%3E%3E(element.elementIterator(),%0A                        new Dom4jElementWrappe
 r())
@@ -1632,56 +1632,139 @@
 new 
-Dom4jAttributesIterator(element.attributeIterato
+TransformingIterator%3CAttribute, Dom4jNode%3CAttribute%3E%3E(element.attributeIterator(),%0A                        new Dom4jAttributeWrappe
 r())
@@ -2741,24 +2741,22 @@
 ttribute
-sIterato
+Wrappe
 r implem
@@ -2764,398 +2764,132 @@
 nts 
-Iterator%3CDom4jNode%3CAttribute%3E%3E %7B%0A%0A        private final Iterator%3CAttribute%3E elementIterator;%0A%0A        Dom4jAttributesIterator(Iterator%3CAttribute%3E elementIterator) %7B%0A            this.elementIterator = elementIterator;%0A        %7D%0A%0A        @Override%0A        public boolean hasNext() %7B%0A            return elementIterator.hasNext();%0A        %7D%0A%0A        @Override%0A        public Dom4jNode%3CA
+Function%3CAttribute, Dom4jNode%3CAttribute%3E%3E %7B%0A%0A        @Override%0A        public Dom4jNode%3CAttribute%3E apply(Attribute a
 ttribute
 %3E ne
@@ -2884,23 +2884,16 @@
 ttribute
-%3E next(
 ) %7B%0A    
@@ -2930,128 +2930,17 @@
 ute(
-elementIterator.next());%0A        %7D%0A%0A        @Override%0A        public void remove() %7B%0A            elementIterator.remove(
+attribute
 );%0A 
@@ -2999,24 +2999,22 @@
 jElement
-sIterato
+Wrappe
 r implem
@@ -3022,555 +3022,169 @@
 nts 
-Iterator%3CDom4jNode%3CElement%3E%3E %7B%0A%0A        private final Iterator%3CElement%3E elementIterator;%0A%0A        Dom4jElementsIterator(Iterator%3CElement%3E elementIterator) %7B%0A            this.elementIterator = elementIterator;%0A        %7D%0A%0A        @Override%0A        public boolean hasNext() %7B%0A            return elementIterator.hasNext();%0A        %7D%0A%0A        @Override%0A        public Dom4jNode%3CElement%3E next() %7B%0A            return new Dom4jElement(elementIterator.next());%0A        %7D%0A%0A        @Override%0A        public void remove() %7B%0A            elementIterator.remove(
+Function%3CElement, Dom4jNode%3CElement%3E%3E %7B%0A%0A        @Override%0A        public Dom4jNode%3CElement%3E apply(Element element) %7B%0A            return new Dom4jElement(element
 );%0A 

Simplify Dom4J by using core utils.