View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *     http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.commons.jxpath.ri.model.dom;
19  
20  import java.util.ArrayList;
21  import java.util.List;
22  import java.util.Objects;
23  
24  import org.apache.commons.jxpath.ri.QName;
25  import org.apache.commons.jxpath.ri.model.NodeIterator;
26  import org.apache.commons.jxpath.ri.model.NodePointer;
27  import org.w3c.dom.Attr;
28  import org.w3c.dom.Element;
29  import org.w3c.dom.NamedNodeMap;
30  import org.w3c.dom.Node;
31  
32  /**
33   * An iterator of attributes of a DOM Node.
34   */
35  public class DOMAttributeIterator implements NodeIterator {
36  
37      private final NodePointer parent;
38      private final QName qName;
39      private final List<Attr> attributes;
40      private int position;
41  
42      /**
43       * Constructs a new DOMAttributeIterator.
44       *
45       * @param parent pointer
46       * @param qName   to test
47       */
48      public DOMAttributeIterator(final NodePointer parent, final QName qName) {
49          this.parent = parent;
50          this.qName = qName;
51          attributes = new ArrayList<>();
52          final Node node = (Node) parent.getNode();
53          if (node.getNodeType() == Node.ELEMENT_NODE) {
54              final String lname = qName.getName();
55              if (!lname.equals("*")) {
56                  final Attr attr = getAttribute((Element) node, qName);
57                  if (attr != null) {
58                      attributes.add(attr);
59                  }
60              } else {
61                  final NamedNodeMap map = node.getAttributes();
62                  final int count = map.getLength();
63                  for (int i = 0; i < count; i++) {
64                      final Attr attr = (Attr) map.item(i);
65                      if (testAttr(attr)) {
66                          attributes.add(attr);
67                      }
68                  }
69              }
70          }
71      }
72  
73      /**
74       * Gets the named attribute.
75       *
76       * @param element to search
77       * @param qName    to match
78       * @return Attr found
79       */
80      private Attr getAttribute(final Element element, final QName qName) {
81          final String testPrefix = qName.getPrefix();
82          String testNS = null;
83          if (testPrefix != null) {
84              testNS = parent.getNamespaceResolver().getNamespaceURI(testPrefix);
85          }
86          if (testNS != null) {
87              Attr attr = element.getAttributeNodeNS(testNS, qName.getName());
88              if (attr != null) {
89                  return attr;
90              }
91              // This may mean that the parser does not support NS for
92              // attributes, example - the version of Crimson bundled
93              // with JDK 1.4.0
94              final NamedNodeMap nnm = element.getAttributes();
95              for (int i = 0; i < nnm.getLength(); i++) {
96                  attr = (Attr) nnm.item(i);
97                  if (testAttr(attr)) {
98                      return attr;
99                  }
100             }
101             return null;
102         }
103         return element.getAttributeNode(qName.getName());
104     }
105 
106     @Override
107     public NodePointer getNodePointer() {
108         if (position == 0) {
109             if (!setPosition(1)) {
110                 return null;
111             }
112             position = 0;
113         }
114         int index = position - 1;
115         if (index < 0) {
116             index = 0;
117         }
118         return new DOMAttributePointer(parent, attributes.get(index));
119     }
120 
121     @Override
122     public int getPosition() {
123         return position;
124     }
125 
126     @Override
127     public boolean setPosition(final int position) {
128         this.position = position;
129         return position >= 1 && position <= attributes.size();
130     }
131 
132     /**
133      * Test an attribute.
134      *
135      * @param attr to test
136      * @return whether test succeeded
137      */
138     private boolean testAttr(final Attr attr) {
139         final String nodePrefix = DOMNodePointer.getPrefix(attr);
140         final String nodeLocalName = DOMNodePointer.getLocalName(attr);
141         if (nodePrefix != null && nodePrefix.equals("xmlns")) {
142             return false;
143         }
144         if (nodePrefix == null && nodeLocalName.equals("xmlns")) {
145             return false;
146         }
147         final String testLocalName = qName.getName();
148         if (testLocalName.equals("*") || testLocalName.equals(nodeLocalName)) {
149             final String testPrefix = qName.getPrefix();
150             if (testPrefix == null || Objects.equals(testPrefix, nodePrefix)) {
151                 return true;
152             }
153             if (nodePrefix == null) {
154                 return false;
155             }
156             return Objects.equals(parent.getNamespaceURI(testPrefix), parent.getNamespaceURI(nodePrefix));
157         }
158         return false;
159     }
160 }