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; 022import java.util.Objects; 023 024import org.apache.commons.jxpath.ri.QName; 025import org.apache.commons.jxpath.ri.model.NodeIterator; 026import org.apache.commons.jxpath.ri.model.NodePointer; 027import org.w3c.dom.Attr; 028import org.w3c.dom.Element; 029import org.w3c.dom.NamedNodeMap; 030import org.w3c.dom.Node; 031 032/** 033 * An iterator of attributes of a DOM Node. 034 */ 035public class DOMAttributeIterator implements NodeIterator { 036 037 private final NodePointer parent; 038 private final QName qName; 039 private final List<Attr> attributes; 040 private int position; 041 042 /** 043 * Constructs a new DOMAttributeIterator. 044 * 045 * @param parent pointer 046 * @param qName to test 047 */ 048 public DOMAttributeIterator(final NodePointer parent, final QName qName) { 049 this.parent = parent; 050 this.qName = qName; 051 attributes = new ArrayList<>(); 052 final Node node = (Node) parent.getNode(); 053 if (node.getNodeType() == Node.ELEMENT_NODE) { 054 final String lname = qName.getName(); 055 if (!lname.equals("*")) { 056 final Attr attr = getAttribute((Element) node, qName); 057 if (attr != null) { 058 attributes.add(attr); 059 } 060 } else { 061 final NamedNodeMap map = node.getAttributes(); 062 final int count = map.getLength(); 063 for (int i = 0; i < count; i++) { 064 final Attr attr = (Attr) map.item(i); 065 if (testAttr(attr)) { 066 attributes.add(attr); 067 } 068 } 069 } 070 } 071 } 072 073 /** 074 * Gets the named attribute. 075 * 076 * @param element to search 077 * @param qName to match 078 * @return Attr found 079 */ 080 private Attr getAttribute(final Element element, final QName qName) { 081 final String testPrefix = qName.getPrefix(); 082 String testNS = null; 083 if (testPrefix != null) { 084 testNS = parent.getNamespaceResolver().getNamespaceURI(testPrefix); 085 } 086 if (testNS != null) { 087 Attr attr = element.getAttributeNodeNS(testNS, qName.getName()); 088 if (attr != null) { 089 return attr; 090 } 091 // This may mean that the parser does not support NS for 092 // attributes, example - the version of Crimson bundled 093 // with JDK 1.4.0 094 final NamedNodeMap nnm = element.getAttributes(); 095 for (int i = 0; i < nnm.getLength(); i++) { 096 attr = (Attr) nnm.item(i); 097 if (testAttr(attr)) { 098 return attr; 099 } 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}