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 */
017package org.apache.commons.jxpath.ri.model.dom;
018
019import java.util.ArrayList;
020import java.util.List;
021
022import org.apache.commons.jxpath.ri.QName;
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.Element;
027import org.w3c.dom.NamedNodeMap;
028import org.w3c.dom.Node;
029
030/**
031 * An iterator of attributes of a DOM Node.
032 *
033 * @author Dmitri Plotnikov
034 * @version $Revision: 652845 $ $Date: 2008-05-02 19:46:46 +0200 (Fr, 02 Mai 2008) $
035 */
036public class DOMAttributeIterator implements NodeIterator {
037    private NodePointer parent;
038    private QName name;
039    private List attributes;
040    private int position = 0;
041
042    /**
043     * Create a new DOMAttributeIterator.
044     * @param parent pointer
045     * @param name to test
046     */
047    public DOMAttributeIterator(NodePointer parent, QName name) {
048        this.parent = parent;
049        this.name = name;
050        attributes = new ArrayList();
051        Node node = (Node) parent.getNode();
052        if (node.getNodeType() == Node.ELEMENT_NODE) {
053            String lname = name.getName();
054            if (!lname.equals("*")) {
055                Attr attr = getAttribute((Element) node, name);
056                if (attr != null) {
057                    attributes.add(attr);
058                }
059            }
060            else {
061                NamedNodeMap map = node.getAttributes();
062                int count = map.getLength();
063                for (int i = 0; i < count; i++) {
064                    Attr attr = (Attr) map.item(i);
065                    if (testAttr(attr)) {
066                        attributes.add(attr);
067                    }
068                }
069            }
070        }
071    }
072
073    /**
074     * Test an attribute.
075     * @param attr to test
076     * @return whether test succeeded
077     */
078    private boolean testAttr(Attr attr) {
079        String nodePrefix = DOMNodePointer.getPrefix(attr);
080        String nodeLocalName = DOMNodePointer.getLocalName(attr);
081
082        if (nodePrefix != null && nodePrefix.equals("xmlns")) {
083            return false;
084        }
085
086        if (nodePrefix == null && nodeLocalName.equals("xmlns")) {
087            return false;
088        }
089
090        String testLocalName = name.getName();
091        if (testLocalName.equals("*") || testLocalName.equals(nodeLocalName)) {
092            String testPrefix = name.getPrefix();
093
094            if (testPrefix == null || equalStrings(testPrefix, nodePrefix)) {
095                return true;
096            }
097            if (nodePrefix == null) {
098                return false;
099            }
100            return equalStrings(parent.getNamespaceURI(testPrefix), parent
101                    .getNamespaceURI(nodePrefix));
102        }
103        return false;
104    }
105
106    /**
107     * Test whether two strings are == or .equals()
108     * @param s1 first string
109     * @param s2 second string
110     * @return boolean
111     */
112    private static boolean equalStrings(String s1, String s2) {
113        return s1 == s2 || s1 != null && s1.equals(s2);
114    }
115
116    /**
117     * Get the named attribute.
118     * @param element to search
119     * @param name to match
120     * @return Attr found
121     */
122    private Attr getAttribute(Element element, QName name) {
123        String testPrefix = name.getPrefix();
124        String testNS = null;
125
126        if (testPrefix != null) {
127            testNS = parent.getNamespaceResolver().getNamespaceURI(testPrefix);
128        }
129
130        if (testNS != null) {
131            Attr attr = element.getAttributeNodeNS(testNS, name.getName());
132            if (attr != null) {
133                return attr;
134            }
135
136            // This may mean that the parser does not support NS for
137            // attributes, example - the version of Crimson bundled
138            // with JDK 1.4.0
139            NamedNodeMap nnm = element.getAttributes();
140            for (int i = 0; i < nnm.getLength(); i++) {
141                attr = (Attr) nnm.item(i);
142                if (testAttr(attr)) {
143                    return attr;
144                }
145            }
146            return null;
147        }
148        return element.getAttributeNode(name.getName());
149    }
150
151    public NodePointer getNodePointer() {
152        if (position == 0) {
153            if (!setPosition(1)) {
154                return null;
155            }
156            position = 0;
157        }
158        int index = position - 1;
159        if (index < 0) {
160            index = 0;
161        }
162        return new DOMAttributePointer(parent, (Attr) attributes.get(index));
163    }
164
165    public int getPosition() {
166        return position;
167    }
168
169    public boolean setPosition(int position) {
170        this.position = position;
171        return position >= 1 && position <= attributes.size();
172    }
173}