2222package org .apache .jcp .xml .dsig .internal .dom ;
2323
2424import java .security .spec .AlgorithmParameterSpec ;
25+ import java .util .Arrays ;
2526import java .util .List ;
2627
2728import javax .xml .XMLConstants ;
3940import org .w3c .dom .Document ;
4041import org .w3c .dom .Element ;
4142import org .w3c .dom .Node ;
43+ import org .w3c .dom .NodeList ;
4244
4345/**
4446 * Useful static DOM utility methods.
@@ -49,6 +51,9 @@ public final class DOMUtils {
4951 // class cannot be instantiated
5052 private DOMUtils () {}
5153
54+ /** Attribute names treated as XML ID attributes when scanning for ID declarations. */
55+ private static final List <String > ID_ATTRIBUTE_NAMES = Arrays .asList ("Id" , "ID" , "id" );
56+
5257 /**
5358 * Returns the owner document of the specified node.
5459 *
@@ -92,7 +97,7 @@ public static Element createElement(Document doc, String tag,
9297 String nsURI , String prefix )
9398 {
9499 String qName = (prefix == null || prefix .length () == 0 )
95- ? tag : prefix + ":" + tag ;
100+ ? tag : prefix + ":" + tag ;
96101 return doc .createElementNS (nsURI , qName );
97102 }
98103
@@ -158,23 +163,23 @@ public static Element getFirstChildElement(Node node) {
158163 * equal to {@code localName}
159164 */
160165 public static Element getFirstChildElement (Node node , String localName , String namespaceURI )
161- throws MarshalException
166+ throws MarshalException
162167 {
163168 return verifyElement (getFirstChildElement (node ), localName , namespaceURI );
164169 }
165170
166171 private static Element verifyElement (Element elem , String localName , String namespaceURI )
167- throws MarshalException
172+ throws MarshalException
168173 {
169174 if (elem == null ) {
170175 throw new MarshalException ("Missing " + localName + " element" );
171176 }
172177 String name = elem .getLocalName ();
173178 String namespace = elem .getNamespaceURI ();
174179 if (!name .equals (localName ) || namespace == null && namespaceURI != null
175- || namespace != null && !namespace .equals (namespaceURI )) {
180+ || namespace != null && !namespace .equals (namespaceURI )) {
176181 throw new MarshalException ("Invalid element name: " +
177- namespace + ":" + name + ", expected " + namespaceURI + ":" + localName );
182+ namespace + ":" + name + ", expected " + namespaceURI + ":" + localName );
178183 }
179184 return elem ;
180185 }
@@ -225,7 +230,7 @@ public static Element getNextSiblingElement(Node node) {
225230 * equal to {@code localName}
226231 */
227232 public static Element getNextSiblingElement (Node node , String localName , String namespaceURI )
228- throws MarshalException
233+ throws MarshalException
229234 {
230235 return verifyElement (getNextSiblingElement (node ), localName , namespaceURI );
231236 }
@@ -282,7 +287,7 @@ public static <N> String getIdAttributeValue(Element elem, String name) {
282287 public static String getNSPrefix (XMLCryptoContext context , String nsURI ) {
283288 if (context != null ) {
284289 return context .getNamespacePrefix
285- (nsURI , context .getDefaultNamespacePrefix ());
290+ (nsURI , context .getDefaultNamespacePrefix ());
286291 } else {
287292 return null ;
288293 }
@@ -335,29 +340,29 @@ public static void appendChild(Node parent, Node child) {
335340 }
336341
337342 public static boolean paramsEqual (AlgorithmParameterSpec spec1 ,
338- AlgorithmParameterSpec spec2 ) {
343+ AlgorithmParameterSpec spec2 ) {
339344 if (spec1 == spec2 ) {
340345 return true ;
341346 }
342347 if (spec1 instanceof XPathFilter2ParameterSpec &&
343- spec2 instanceof XPathFilter2ParameterSpec ) {
348+ spec2 instanceof XPathFilter2ParameterSpec ) {
344349 return paramsEqual ((XPathFilter2ParameterSpec )spec1 ,
345- (XPathFilter2ParameterSpec )spec2 );
350+ (XPathFilter2ParameterSpec )spec2 );
346351 }
347352 if (spec1 instanceof ExcC14NParameterSpec &&
348- spec2 instanceof ExcC14NParameterSpec ) {
353+ spec2 instanceof ExcC14NParameterSpec ) {
349354 return paramsEqual ((ExcC14NParameterSpec ) spec1 ,
350- (ExcC14NParameterSpec )spec2 );
355+ (ExcC14NParameterSpec )spec2 );
351356 }
352357 if (spec1 instanceof XPathFilterParameterSpec &&
353- spec2 instanceof XPathFilterParameterSpec ) {
358+ spec2 instanceof XPathFilterParameterSpec ) {
354359 return paramsEqual ((XPathFilterParameterSpec )spec1 ,
355- (XPathFilterParameterSpec )spec2 );
360+ (XPathFilterParameterSpec )spec2 );
356361 }
357362 if (spec1 instanceof XSLTTransformParameterSpec &&
358- spec2 instanceof XSLTTransformParameterSpec ) {
363+ spec2 instanceof XSLTTransformParameterSpec ) {
359364 return paramsEqual ((XSLTTransformParameterSpec )spec1 ,
360- (XSLTTransformParameterSpec )spec2 );
365+ (XSLTTransformParameterSpec )spec2 );
361366 }
362367 return false ;
363368 }
@@ -377,8 +382,8 @@ private static boolean paramsEqual(XPathFilter2ParameterSpec spec1,
377382 XPathType type = types .get (i );
378383 XPathType otype = otypes .get (i );
379384 if (!type .getExpression ().equals (otype .getExpression ()) ||
380- !type .getNamespaceMap ().equals (otype .getNamespaceMap ()) ||
381- type .getFilter () != otype .getFilter ()) {
385+ !type .getNamespaceMap ().equals (otype .getNamespaceMap ()) ||
386+ type .getFilter () != otype .getFilter ()) {
382387 return false ;
383388 }
384389 }
@@ -407,10 +412,10 @@ private static boolean paramsEqual(XSLTTransformParameterSpec spec1,
407412 return false ;
408413 }
409414 Node ostylesheetElem =
410- ((javax .xml .crypto .dom .DOMStructure ) ostylesheet ).getNode ();
415+ ((javax .xml .crypto .dom .DOMStructure ) ostylesheet ).getNode ();
411416 XMLStructure stylesheet = spec1 .getStylesheet ();
412417 Node stylesheetElem =
413- ((javax .xml .crypto .dom .DOMStructure ) stylesheet ).getNode ();
418+ ((javax .xml .crypto .dom .DOMStructure ) stylesheet ).getNode ();
414419 return nodesEqual (stylesheetElem , ostylesheetElem );
415420 }
416421
@@ -422,4 +427,40 @@ public static boolean isNamespace(Node node)
422427 }
423428 return false ;
424429 }
430+
431+ /**
432+ * Recursively walks the DOM tree rooted at {@code node} and calls
433+ * {@link Element#setIdAttribute(String, boolean)} for every attribute whose local name
434+ * is one of {@code Id}, {@code ID}, or {@code id}. This is required so that
435+ * {@link Document#getElementById} correctly resolves same-document ID references in
436+ * XAdES-generated structures.
437+ *
438+ * @param node the root node to start from
439+ */
440+ public static void setIdFlagToIdAttributes (Node node ) {
441+ setIdFlagToIdAttributes (node , ID_ATTRIBUTE_NAMES );
442+ }
443+
444+ /**
445+ * Recursively walks the DOM tree rooted at {@code node} and calls
446+ * {@link Element#setIdAttribute(String, boolean)} for every attribute whose local name
447+ * is in {@code idAttributeNames}.
448+ *
449+ * @param node the root node to start from
450+ * @param idAttributeNames the list of attribute local names to treat as Id attributes
451+ */
452+ public static void setIdFlagToIdAttributes (Node node , List <String > idAttributeNames ) {
453+ if (node .getNodeType () == Node .ELEMENT_NODE ) {
454+ Element element = (Element ) node ;
455+ for (String idName : idAttributeNames ) {
456+ if (element .hasAttribute (idName )) {
457+ element .setIdAttribute (idName , true );
458+ }
459+ }
460+ NodeList children = element .getChildNodes ();
461+ for (int i = 0 ; i < children .getLength (); i ++) {
462+ setIdFlagToIdAttributes (children .item (i ), idAttributeNames );
463+ }
464+ }
465+ }
425466}
0 commit comments