1 /* 2 * reserved comment block 3 * DO NOT REMOVE OR ALTER! 4 */ 5 /** 6 * Licensed to the Apache Software Foundation (ASF) under one 7 * or more contributor license agreements. See the NOTICE file 8 * distributed with this work for additional information 9 * regarding copyright ownership. The ASF licenses this file 10 * to you under the Apache License, Version 2.0 (the 11 * "License"); you may not use this file except in compliance 12 * with the License. You may obtain a copy of the License at 13 * 14 * http://www.apache.org/licenses/LICENSE-2.0 15 * 16 * Unless required by applicable law or agreed to in writing, 17 * software distributed under the License is distributed on an 18 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 19 * KIND, either express or implied. See the License for the 20 * specific language governing permissions and limitations 21 * under the License. 22 */ 23 /* 24 * Copyright (c) 2005, 2008, Oracle and/or its affiliates. All rights reserved. 25 */ 26 /* 27 * $Id$ 28 */ 29 package org.jcp.xml.dsig.internal.dom; 30 31 import javax.xml.crypto.NodeSetData; 32 import java.util.ArrayList; 33 import java.util.Iterator; 34 import java.util.List; 35 import java.util.ListIterator; 36 import java.util.NoSuchElementException; 37 import org.w3c.dom.NamedNodeMap; 38 import org.w3c.dom.Node; 39 40 /** 41 * This is a subtype of NodeSetData that represents a dereferenced 42 * same-document URI as the root of a subdocument. The main reason is 43 * for efficiency and performance, as some transforms can operate 44 * directly on the subdocument and there is no need to convert it 45 * first to an XPath node-set. 46 */ 47 public class DOMSubTreeData implements NodeSetData { 48 49 private boolean excludeComments; 50 private Node root; 51 52 public DOMSubTreeData(Node root, boolean excludeComments) { 53 this.root = root; 54 this.excludeComments = excludeComments; 55 } 56 57 public Iterator iterator() { 58 return new DelayedNodeIterator(root, excludeComments); 59 } 60 61 public Node getRoot() { 62 return root; 63 } 64 65 public boolean excludeComments() { 66 return excludeComments; 67 } 68 69 /** 70 * This is an Iterator that contains a backing node-set that is 71 * not populated until the caller first attempts to advance the iterator. 72 */ 73 static class DelayedNodeIterator implements Iterator<Node> { 74 private Node root; 75 private List<Node> nodeSet; 76 private ListIterator<Node> li; 77 private boolean withComments; 78 79 DelayedNodeIterator(Node root, boolean excludeComments) { 80 this.root = root; 81 this.withComments = !excludeComments; 82 } 83 84 public boolean hasNext() { 85 if (nodeSet == null) { 86 nodeSet = dereferenceSameDocumentURI(root); 87 li = nodeSet.listIterator(); 88 } 89 return li.hasNext(); 90 } 91 92 public Node next() { 93 if (nodeSet == null) { 94 nodeSet = dereferenceSameDocumentURI(root); 95 li = nodeSet.listIterator(); 96 } 97 if (li.hasNext()) { 98 return li.next(); 99 } else { 100 throw new NoSuchElementException(); 101 } 102 } 103 104 public void remove() { 105 throw new UnsupportedOperationException(); 106 } 107 108 /** 109 * Dereferences a same-document URI fragment. 110 * 111 * @param node the node (document or element) referenced by the 112 * URI fragment. If null, returns an empty set. 113 * @return a set of nodes (minus any comment nodes) 114 */ 115 private List<Node> dereferenceSameDocumentURI(Node node) { 116 List<Node> nodeSet = new ArrayList<Node>(); 117 if (node != null) { 118 nodeSetMinusCommentNodes(node, nodeSet, null); 119 } 120 return nodeSet; 121 } 122 123 /** 124 * Recursively traverses the subtree, and returns an XPath-equivalent 125 * node-set of all nodes traversed, excluding any comment nodes, 126 * if specified. 127 * 128 * @param node the node to traverse 129 * @param nodeSet the set of nodes traversed so far 130 * @param the previous sibling node 131 */ 132 @SuppressWarnings("fallthrough") 133 private void nodeSetMinusCommentNodes(Node node, List<Node> nodeSet, 134 Node prevSibling) 135 { 136 switch (node.getNodeType()) { 137 case Node.ELEMENT_NODE : 138 NamedNodeMap attrs = node.getAttributes(); 139 if (attrs != null) { 140 for (int i = 0, len = attrs.getLength(); i < len; i++) { 141 nodeSet.add(attrs.item(i)); 142 } 143 } 144 nodeSet.add(node); 145 Node pSibling = null; 146 for (Node child = node.getFirstChild(); child != null; 147 child = child.getNextSibling()) { 148 nodeSetMinusCommentNodes(child, nodeSet, pSibling); 149 pSibling = child; 150 } 151 break; 152 case Node.DOCUMENT_NODE : 153 pSibling = null; 154 for (Node child = node.getFirstChild(); child != null; 155 child = child.getNextSibling()) { 156 nodeSetMinusCommentNodes(child, nodeSet, pSibling); 157 pSibling = child; 158 } 159 break; 160 case Node.TEXT_NODE : 161 case Node.CDATA_SECTION_NODE: 162 // emulate XPath which only returns the first node in 163 // contiguous text/cdata nodes 164 if (prevSibling != null && 165 (prevSibling.getNodeType() == Node.TEXT_NODE || 166 prevSibling.getNodeType() == Node.CDATA_SECTION_NODE)) { 167 return; 168 } 169 nodeSet.add(node); 170 break; 171 case Node.PROCESSING_INSTRUCTION_NODE : 172 nodeSet.add(node); 173 break; 174 case Node.COMMENT_NODE: 175 if (withComments) { 176 nodeSet.add(node); 177 } 178 } 179 } 180 } 181 }