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    package org.apache.commons.jxpath.ri.model.dom;
018    
019    import java.util.ArrayList;
020    import java.util.List;
021    
022    import org.apache.commons.jxpath.ri.QName;
023    import org.apache.commons.jxpath.ri.model.NodeIterator;
024    import org.apache.commons.jxpath.ri.model.NodePointer;
025    import org.w3c.dom.Attr;
026    import org.w3c.dom.Element;
027    import org.w3c.dom.NamedNodeMap;
028    import 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 12:46:46 -0500 (Fri, 02 May 2008) $
035     */
036    public 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    }