001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *     http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017
018package org.apache.commons.jxpath.ri.model.dom;
019
020import java.util.ArrayList;
021import java.util.List;
022
023import org.apache.commons.jxpath.ri.model.NodeIterator;
024import org.apache.commons.jxpath.ri.model.NodePointer;
025import org.w3c.dom.Attr;
026import org.w3c.dom.Document;
027import org.w3c.dom.NamedNodeMap;
028import org.w3c.dom.Node;
029
030/**
031 * An iterator of namespaces of a DOM Node.
032 */
033public class DOMNamespaceIterator implements NodeIterator {
034
035    private final NodePointer parent;
036    private final List<Attr> attributes;
037    private int position;
038
039    /**
040     * Constructs a new DOMNamespaceIterator.
041     *
042     * @param parent parent pointer
043     */
044    public DOMNamespaceIterator(final NodePointer parent) {
045        this.parent = parent;
046        attributes = new ArrayList<>();
047        collectNamespaces(attributes, (Node) parent.getNode());
048    }
049
050    /**
051     * Collect namespaces from attribute nodes.
052     *
053     * @param attributes attribute list
054     * @param node       target node
055     */
056    private void collectNamespaces(final List<Attr> attributes, Node node) {
057        final Node parent = node.getParentNode();
058        if (parent != null) {
059            collectNamespaces(attributes, parent);
060        }
061        if (node.getNodeType() == Node.DOCUMENT_NODE) {
062            node = ((Document) node).getDocumentElement();
063        }
064        if (node.getNodeType() == Node.ELEMENT_NODE) {
065            final NamedNodeMap map = node.getAttributes();
066            final int count = map.getLength();
067            for (int i = 0; i < count; i++) {
068                final Attr attr = (Attr) map.item(i);
069                final String prefix = DOMNodePointer.getPrefix(attr);
070                final String name = DOMNodePointer.getLocalName(attr);
071                if (prefix != null && prefix.equals("xmlns") || prefix == null && name.equals("xmlns")) {
072                    attributes.add(attr);
073                }
074            }
075        }
076    }
077
078    @Override
079    public NodePointer getNodePointer() {
080        if (position == 0) {
081            if (!setPosition(1)) {
082                return null;
083            }
084            position = 0;
085        }
086        int index = position - 1;
087        if (index < 0) {
088            index = 0;
089        }
090        String prefix = "";
091        final Attr attr = attributes.get(index);
092        final String name = attr.getPrefix();
093        if (name != null && name.equals("xmlns")) {
094            prefix = DOMNodePointer.getLocalName(attr);
095        }
096        return new NamespacePointer(parent, prefix, attr.getValue());
097    }
098
099    @Override
100    public int getPosition() {
101        return position;
102    }
103
104    @Override
105    public boolean setPosition(final int position) {
106        this.position = position;
107        return position >= 1 && position <= attributes.size();
108    }
109}