1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.opensaml.xml.util;
18
19 import java.io.StringWriter;
20 import java.io.Writer;
21 import java.util.ArrayList;
22 import java.util.GregorianCalendar;
23 import java.util.HashMap;
24 import java.util.List;
25 import java.util.Locale;
26 import java.util.Map;
27 import java.util.StringTokenizer;
28
29 import javax.xml.datatype.DatatypeConfigurationException;
30 import javax.xml.datatype.DatatypeFactory;
31 import javax.xml.datatype.Duration;
32 import javax.xml.namespace.QName;
33 import javax.xml.transform.OutputKeys;
34 import javax.xml.transform.Transformer;
35 import javax.xml.transform.TransformerException;
36 import javax.xml.transform.TransformerFactory;
37 import javax.xml.transform.dom.DOMSource;
38 import javax.xml.transform.stream.StreamResult;
39
40 import org.opensaml.xml.XMLObject;
41 import org.opensaml.xml.XMLRuntimeException;
42 import org.opensaml.xml.parse.XMLParserException;
43 import org.w3c.dom.Attr;
44 import org.w3c.dom.DOMImplementation;
45 import org.w3c.dom.Document;
46 import org.w3c.dom.Element;
47 import org.w3c.dom.NamedNodeMap;
48 import org.w3c.dom.Node;
49 import org.w3c.dom.NodeList;
50 import org.w3c.dom.Text;
51 import org.w3c.dom.ls.DOMImplementationLS;
52 import org.w3c.dom.ls.LSOutput;
53 import org.w3c.dom.ls.LSSerializer;
54
55
56
57
58 public final class XMLHelper {
59
60
61 private static DatatypeFactory dataTypeFactory;
62
63
64 private XMLHelper() {
65
66 }
67
68
69
70
71
72
73 public static DatatypeFactory getDataTypeFactory() {
74 if (dataTypeFactory == null) {
75 try {
76 dataTypeFactory = DatatypeFactory.newInstance();
77 } catch (DatatypeConfigurationException e) {
78
79 }
80 }
81
82 return dataTypeFactory;
83 }
84
85
86
87
88
89
90
91
92 public static boolean hasXSIType(Element e) {
93 if (e != null) {
94 if (e.getAttributeNodeNS(XMLConstants.XSI_NS, "type") != null) {
95 return true;
96 }
97 }
98
99 return false;
100 }
101
102
103
104
105
106
107
108
109 public static QName getXSIType(Element e) {
110 if (hasXSIType(e)) {
111 Attr attribute = e.getAttributeNodeNS(XMLConstants.XSI_NS, "type");
112 String attributeValue = attribute.getTextContent().trim();
113 StringTokenizer tokenizer = new StringTokenizer(attributeValue, ":");
114 String prefix = null;
115 String localPart;
116 if (tokenizer.countTokens() > 1) {
117 prefix = tokenizer.nextToken();
118 localPart = tokenizer.nextToken();
119 } else {
120 localPart = tokenizer.nextToken();
121 }
122
123 return constructQName(e.lookupNamespaceURI(prefix), localPart, prefix);
124 }
125
126 return null;
127 }
128
129
130
131
132
133
134
135
136 public static Attr getIdAttribute(Element domElement) {
137 if (!domElement.hasAttributes()) {
138 return null;
139 }
140
141 NamedNodeMap attributes = domElement.getAttributes();
142 Attr attribute;
143 for (int i = 0; i < attributes.getLength(); i++) {
144 attribute = (Attr) attributes.item(i);
145 if (attribute.isId()) {
146 return attribute;
147 }
148 }
149
150 return null;
151 }
152
153
154
155
156
157
158
159
160 public static QName getNodeQName(Node domNode) {
161 if (domNode != null) {
162 return constructQName(domNode.getNamespaceURI(), domNode.getLocalName(), domNode.getPrefix());
163 }
164
165 return null;
166 }
167
168
169
170
171
172
173
174
175
176
177 public static Locale getLanguage(Element element) {
178 String lang = DatatypeHelper.safeTrimOrNullString(element.getAttributeNS(XMLConstants.XML_NS, "lang"));
179 if(lang != null){
180 if(lang.contains("-")){
181 lang = lang.substring(0, lang.indexOf("-"));
182 }
183 return new Locale(lang.toUpperCase());
184 }else{
185 return Locale.getDefault();
186 }
187 }
188
189
190
191
192
193
194
195
196
197 public static Attr constructAttribute(Document owningDocument, QName attributeName) {
198 return constructAttribute(owningDocument, attributeName.getNamespaceURI(), attributeName.getLocalPart(),
199 attributeName.getPrefix());
200 }
201
202
203
204
205
206
207
208
209
210
211
212 public static Attr constructAttribute(Document document, String namespaceURI, String localName, String prefix) {
213 String trimmedLocalName = DatatypeHelper.safeTrimOrNullString(localName);
214
215 if (trimmedLocalName == null) {
216 throw new IllegalArgumentException("Local name may not be null or empty");
217 }
218
219 String qualifiedName;
220 String trimmedPrefix = DatatypeHelper.safeTrimOrNullString(prefix);
221 if (trimmedPrefix != null) {
222 qualifiedName = trimmedPrefix + ":" + DatatypeHelper.safeTrimOrNullString(trimmedLocalName);
223 } else {
224 qualifiedName = DatatypeHelper.safeTrimOrNullString(trimmedLocalName);
225 }
226
227 if (DatatypeHelper.isEmpty(namespaceURI)) {
228 return document.createAttribute(qualifiedName);
229 } else {
230 return document.createAttributeNS(namespaceURI, qualifiedName);
231 }
232 }
233
234
235
236
237
238
239
240
241 public static QName getAttributeValueAsQName(Attr attribute) {
242 if (attribute == null || DatatypeHelper.isEmpty(attribute.getValue())) {
243 return null;
244 }
245
246 String attributeValue = attribute.getTextContent();
247 String[] valueComponents = attributeValue.split(":");
248 if (valueComponents.length == 1) {
249 return constructQName(attribute.lookupNamespaceURI(null), valueComponents[0], null);
250 } else {
251 return constructQName(attribute.lookupNamespaceURI(valueComponents[0]), valueComponents[1],
252 valueComponents[0]);
253 }
254 }
255
256
257
258
259
260
261
262
263
264 public static Boolean getAttributeValueAsBoolean(Attr attribute) {
265 if(attribute == null){
266 return null;
267 }
268
269 String valueStr = attribute.getValue();
270 if (valueStr.equals("0") || valueStr.equals("false")) {
271 return Boolean.FALSE;
272 } else if (valueStr.equals("1") || valueStr.equals("true")) {
273 return Boolean.TRUE;
274 } else {
275 return null;
276 }
277 }
278
279
280
281
282
283
284
285
286 public static List<String> getAttributeValueAsList(Attr attribute) {
287 ArrayList<String> values = new ArrayList<String>();
288 if (attribute == null) {
289 return values;
290 }
291
292 StringTokenizer valueTokens = new StringTokenizer(DatatypeHelper.safeTrimOrNullString(attribute.getValue()));
293 while (valueTokens.hasMoreTokens()) {
294 values.add(valueTokens.nextToken());
295 }
296
297 return values;
298 }
299
300
301
302
303
304
305
306
307 public static QName getElementContentAsQName(Element element) {
308 if (element == null) {
309 return null;
310 }
311
312 String elementContent = null;
313 NodeList nodeList = element.getChildNodes();
314 for (int i = 0; i < nodeList.getLength(); i++) {
315 Node node = nodeList.item(i);
316 if (node.getNodeType() == Node.TEXT_NODE) {
317 elementContent = DatatypeHelper.safeTrimOrNullString(((Text) node).getWholeText());
318 break;
319 }
320 }
321
322 if (elementContent == null) {
323 return null;
324 }
325
326 String[] valueComponents = elementContent.split(":");
327 if (valueComponents.length == 1) {
328 return constructQName(element.lookupNamespaceURI(null), valueComponents[0], null);
329 } else {
330 return constructQName(element.lookupNamespaceURI(valueComponents[0]), valueComponents[1],
331 valueComponents[0]);
332 }
333 }
334
335
336
337
338
339
340
341
342 public static List<String> getElementContentAsList(Element element) {
343 ArrayList<String> values = new ArrayList<String>();
344 if (element == null) {
345 return values;
346 }
347
348 String elementContent = DatatypeHelper.safeTrimOrNullString(element.getTextContent());
349 StringTokenizer valueTokens = new StringTokenizer(elementContent);
350 while (valueTokens.hasMoreTokens()) {
351 values.add(valueTokens.nextToken());
352 }
353
354 return values;
355 }
356
357
358
359
360
361
362
363
364
365
366 public static QName constructQName(String namespaceURI, String localName, String prefix) {
367 if (DatatypeHelper.isEmpty(prefix)) {
368 return new QName(namespaceURI, localName);
369 } else if (DatatypeHelper.isEmpty(namespaceURI)) {
370 return new QName(localName);
371 }
372
373 return new QName(namespaceURI, localName, prefix);
374 }
375
376
377
378
379
380
381
382
383
384 public static QName constructQName(String qname, XMLObject owningObject) {
385 return constructQName(qname, owningObject.getDOM());
386 }
387
388
389
390
391
392
393
394
395
396 public static QName constructQName(String qname, Element owningElement) {
397 String nsURI;
398 String nsPrefix;
399 String name;
400
401 if (qname.indexOf(":") > -1) {
402 StringTokenizer qnameTokens = new StringTokenizer(qname, ":");
403 nsPrefix = qnameTokens.nextToken();
404 name = qnameTokens.nextToken();
405 } else {
406 nsPrefix = "";
407 name = qname;
408 }
409
410 nsURI = lookupNamespaceURI(owningElement, nsPrefix);
411 return constructQName(nsURI, name, nsPrefix);
412 }
413
414
415
416
417
418
419
420
421
422 public static Element constructElement(Document document, QName elementName) {
423 return constructElement(document, elementName.getNamespaceURI(), elementName.getLocalPart(), elementName
424 .getPrefix());
425 }
426
427
428
429
430
431
432
433
434
435
436
437 public static Element constructElement(Document document, String namespaceURI, String localName, String prefix) {
438 String trimmedLocalName = DatatypeHelper.safeTrimOrNullString(localName);
439
440 if (trimmedLocalName == null) {
441 throw new IllegalArgumentException("Local name may not be null or empty");
442 }
443
444 String qualifiedName;
445 String trimmedPrefix = DatatypeHelper.safeTrimOrNullString(prefix);
446 if (trimmedPrefix != null) {
447 qualifiedName = trimmedPrefix + ":" + DatatypeHelper.safeTrimOrNullString(trimmedLocalName);
448 } else {
449 qualifiedName = DatatypeHelper.safeTrimOrNullString(trimmedLocalName);
450 }
451
452 if (!DatatypeHelper.isEmpty(namespaceURI)) {
453 return document.createElementNS(namespaceURI, qualifiedName);
454 } else {
455 return document.createElementNS(null, qualifiedName);
456 }
457 }
458
459
460
461
462
463
464
465 public static void appendChildElement(Element parentElement, Element childElement) {
466 Document parentDocument = parentElement.getOwnerDocument();
467 adoptElement(childElement, parentDocument);
468
469 parentElement.appendChild(childElement);
470 }
471
472
473
474
475
476
477
478 public static void adoptElement(Element adoptee, Document adopter) {
479 if (!(adoptee.getOwnerDocument().equals(adopter))) {
480 if (adopter.adoptNode(adoptee) == null) {
481
482 throw new XMLRuntimeException("DOM Element node adoption failed");
483 }
484 }
485 }
486
487
488
489
490
491
492
493 public static void appendTextContent(Element domElement, String textContent) {
494 if (textContent == null) {
495 return;
496 }
497 Document parentDocument = domElement.getOwnerDocument();
498 Text textNode = parentDocument.createTextNode(textContent);
499 domElement.appendChild(textNode);
500 }
501
502
503
504
505
506
507
508
509 public static void appendNamespaceDeclaration(Element domElement, String namespaceURI, String prefix) {
510 String nsURI = DatatypeHelper.safeTrimOrNullString(namespaceURI);
511 String nsPrefix = DatatypeHelper.safeTrimOrNullString(prefix);
512
513
514 if (nsURI == null && nsPrefix == null) {
515 return;
516 }
517
518 String attributeName;
519 if (nsPrefix == null) {
520 attributeName = XMLConstants.XMLNS_PREFIX;
521 } else {
522 attributeName = XMLConstants.XMLNS_PREFIX + ":" + nsPrefix;
523 }
524
525 String attributeValue;
526 if (nsURI == null) {
527 attributeValue = "";
528 } else {
529 attributeValue = nsURI;
530 }
531
532 domElement.setAttributeNS(XMLConstants.XMLNS_NS, attributeName, attributeValue);
533 }
534
535
536
537
538
539
540
541
542
543
544
545
546
547 public static String lookupNamespaceURI(Element startingElement, String prefix) {
548 return lookupNamespaceURI(startingElement, null, prefix);
549 }
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564 public static String lookupNamespaceURI(Element startingElement, Element stopingElement, String prefix) {
565 String namespaceURI;
566
567 if (startingElement == stopingElement) {
568 return null;
569 }
570
571
572 if (startingElement.hasAttributes()) {
573 NamedNodeMap map = startingElement.getAttributes();
574 int length = map.getLength();
575 for (int i = 0; i < length; i++) {
576 Node attr = map.item(i);
577 String attrPrefix = attr.getPrefix();
578 String value = attr.getNodeValue();
579 namespaceURI = attr.getNamespaceURI();
580 if (namespaceURI != null && namespaceURI.equals(XMLConstants.XMLNS_NS)) {
581
582 if (prefix == null && attr.getNodeName().equals(XMLConstants.XMLNS_PREFIX)) {
583
584 return value;
585 } else if (attrPrefix != null && attrPrefix.equals(XMLConstants.XMLNS_PREFIX)
586 && attr.getLocalName().equals(prefix)) {
587
588 return value;
589 }
590 }
591 }
592 }
593
594 Element ancestor = getElementAncestor(startingElement);
595
596 if (ancestor != null) {
597 return lookupNamespaceURI(ancestor, stopingElement, prefix);
598 }
599
600 return null;
601 }
602
603
604
605
606
607
608
609
610
611
612
613
614
615 public static String lookupPrefix(Element startingElement, String namespaceURI) {
616 return lookupPrefix(startingElement, null, namespaceURI);
617 }
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632 public static String lookupPrefix(Element startingElement, Element stopingElement, String namespaceURI) {
633 String namespace;
634
635 if (startingElement == stopingElement) {
636 return null;
637 }
638
639
640 if (startingElement.hasAttributes()) {
641 NamedNodeMap map = startingElement.getAttributes();
642 int length = map.getLength();
643 for (int i = 0; i < length; i++) {
644 Node attr = map.item(i);
645 String attrPrefix = attr.getPrefix();
646 String value = attr.getNodeValue();
647 namespace = attr.getNamespaceURI();
648 if (namespace != null && namespace.equals(XMLConstants.XMLNS_NS)) {
649
650 if (attr.getNodeName().equals(XMLConstants.XMLNS_PREFIX)
651 || (attrPrefix != null && attrPrefix.equals(XMLConstants.XMLNS_PREFIX))
652 && value.equals(namespaceURI)) {
653
654 String localname = attr.getLocalName();
655 String foundNamespace = startingElement.lookupNamespaceURI(localname);
656 if (foundNamespace != null && foundNamespace.equals(namespaceURI)) {
657 return localname;
658 }
659 }
660
661 }
662 }
663 }
664 Element ancestor = getElementAncestor(startingElement);
665
666 if (ancestor != null) {
667 return lookupPrefix(ancestor, stopingElement, namespaceURI);
668 }
669 return null;
670 }
671
672
673
674
675
676
677
678
679
680
681
682 public static List<Element> getChildElementsByTagNameNS(Element root, String namespaceURI, String localName) {
683 ArrayList<Element> children = new ArrayList<Element>();
684 NodeList childNodes = root.getChildNodes();
685
686 int numOfNodes = childNodes.getLength();
687 Node childNode;
688 Element e;
689 for (int i = 0; i < numOfNodes; i++) {
690 childNode = childNodes.item(i);
691 if (childNode.getNodeType() == Node.ELEMENT_NODE) {
692 e = (Element) childNode;
693 if (e.getNamespaceURI().equals(namespaceURI) && e.getLocalName().equals(localName)) {
694 children.add(e);
695 }
696 }
697 }
698
699 return children;
700 }
701
702
703
704
705
706
707
708
709
710
711 public static List<Element> getChildElementsByTagName(Element root, String localName) {
712 ArrayList<Element> children = new ArrayList<Element>();
713 NodeList childNodes = root.getChildNodes();
714
715 int numOfNodes = childNodes.getLength();
716 Node childNode;
717 Element e;
718 for (int i = 0; i < numOfNodes; i++) {
719 childNode = childNodes.item(i);
720 if (childNode.getNodeType() == Node.ELEMENT_NODE) {
721 e = (Element) childNode;
722 if (e.getLocalName().equals(localName)) {
723 children.add(e);
724 }
725 }
726 }
727
728 return children;
729 }
730
731
732
733
734
735
736
737
738 public static Map<QName, List<Element>> getChildElements(Element root) {
739 Map<QName, List<Element>> children = new HashMap<QName, List<Element>>();
740 NodeList childNodes = root.getChildNodes();
741
742 int numOfNodes = childNodes.getLength();
743 Node childNode;
744 Element e;
745 QName qname;
746 List<Element> elements;
747 for (int i = 0; i < numOfNodes; i++) {
748 childNode = childNodes.item(i);
749 if (childNode.getNodeType() == Node.ELEMENT_NODE) {
750 e = (Element) childNode;
751 qname = getNodeQName(e);
752 elements = children.get(qname);
753 if (elements == null) {
754 elements = new ArrayList<Element>();
755 children.put(qname, elements);
756 }
757
758 elements.add(e);
759 }
760 }
761
762 return children;
763 }
764
765
766
767
768
769
770
771
772 public static Element getElementAncestor(Node currentNode) {
773 Node parent = currentNode.getParentNode();
774 if (parent != null) {
775 short type = parent.getNodeType();
776 if (type == Node.ELEMENT_NODE) {
777 return (Element) parent;
778 }
779 return getElementAncestor(parent);
780 }
781 return null;
782 }
783
784
785
786
787
788
789
790
791 public static String nodeToString(Node node) {
792 StringWriter writer = new StringWriter();
793 writeNode(node, writer);
794 return writer.toString();
795 }
796
797
798
799
800
801
802
803
804 public static String prettyPrintXML(Node node) {
805 TransformerFactory tfactory = TransformerFactory.newInstance();
806 Transformer serializer;
807 try {
808 serializer = tfactory.newTransformer();
809 serializer.setOutputProperty(OutputKeys.INDENT, "yes");
810 serializer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "3");
811
812 StringWriter output = new StringWriter();
813 serializer.transform(new DOMSource(node), new StreamResult(output));
814 return output.toString();
815 } catch (TransformerException e) {
816
817 e.printStackTrace();
818
819 throw new RuntimeException(e);
820 }
821 }
822
823
824
825
826
827
828
829
830 public static void writeNode(Node node, Writer output) {
831 DOMImplementation domImpl = node.getOwnerDocument().getImplementation();
832 DOMImplementationLS domImplLS = (DOMImplementationLS) domImpl.getFeature("LS", "3.0");
833 LSSerializer serializer = domImplLS.createLSSerializer();
834
835 LSOutput serializerOut = domImplLS.createLSOutput();
836 serializerOut.setCharacterStream(output);
837
838 serializer.write(node, serializerOut);
839 }
840
841
842
843
844
845
846
847
848 public static String qnameToContentString(QName qname) {
849 StringBuffer buf = new StringBuffer();
850
851 if (qname.getPrefix() != null) {
852 buf.append(qname.getPrefix());
853 buf.append(":");
854 }
855 buf.append(qname.getLocalPart());
856 return buf.toString();
857 }
858
859
860
861
862
863
864
865
866
867
868
869 public static void rootNamespaces(Element domElement) throws XMLParserException {
870 rootNamespaces(domElement, domElement);
871 }
872
873
874
875
876
877
878
879
880
881
882 private static void rootNamespaces(Element domElement, Element upperNamespaceSearchBound) throws XMLParserException {
883 String namespaceURI = null;
884 String namespacePrefix = null;
885
886
887 namespacePrefix = domElement.getPrefix();
888 if (DatatypeHelper.isEmpty(namespacePrefix)) {
889 namespaceURI = lookupNamespaceURI(upperNamespaceSearchBound, "");
890 } else {
891 namespaceURI = lookupNamespaceURI(domElement, upperNamespaceSearchBound, namespacePrefix);
892 }
893
894 if (namespaceURI == null) {
895 namespaceURI = lookupNamespaceURI(upperNamespaceSearchBound, null, namespacePrefix);
896 if (namespaceURI == null) {
897 throw new XMLParserException("Unable to resolve namespace prefix " + namespacePrefix
898 + " found on element " + getNodeQName(domElement));
899 }
900
901 appendNamespaceDeclaration(domElement, namespaceURI, namespacePrefix);
902 }
903
904
905 NamedNodeMap attributes = domElement.getAttributes();
906 Node attributeNode;
907 for (int i = 0; i < attributes.getLength(); i++) {
908 namespacePrefix = null;
909 namespaceURI = null;
910 attributeNode = attributes.item(i);
911
912
913 if (attributeNode.getNodeType() != Node.ATTRIBUTE_NODE) {
914 continue;
915 }
916
917 namespacePrefix = attributeNode.getPrefix();
918 if (!DatatypeHelper.isEmpty(namespacePrefix)) {
919
920
921 if (namespacePrefix.equals(XMLConstants.XMLNS_PREFIX)
922 || namespacePrefix.equals(XMLConstants.XML_PREFIX)) {
923 continue;
924 }
925
926
927 namespaceURI = lookupNamespaceURI(domElement, upperNamespaceSearchBound, namespacePrefix);
928 if (namespaceURI == null) {
929 namespaceURI = lookupNamespaceURI(upperNamespaceSearchBound, null, namespacePrefix);
930 if (namespaceURI == null) {
931 throw new XMLParserException("Unable to resolve namespace prefix " + namespacePrefix
932 + " found on attribute " + getNodeQName(attributeNode) + " found on element "
933 + getNodeQName(domElement));
934 }
935
936 appendNamespaceDeclaration(domElement, namespaceURI, namespacePrefix);
937 }
938 }
939 }
940
941
942
943 NodeList childNodes = domElement.getChildNodes();
944 Node childNode;
945 for (int i = 0; i < childNodes.getLength(); i++) {
946 childNode = childNodes.item(i);
947 if (childNode.getNodeType() == Node.ELEMENT_NODE) {
948 rootNamespaces((Element) childNode, upperNamespaceSearchBound);
949 }
950 }
951 }
952
953
954
955
956
957
958
959
960
961 public static boolean isElementNamed(Element e, String ns, String localName) {
962 return e != null && DatatypeHelper.safeEquals(ns, e.getNamespaceURI())
963 && DatatypeHelper.safeEquals(localName, e.getLocalName());
964 }
965
966
967
968
969
970
971
972 public static Element getFirstChildElement(Node n) {
973 Node child = n.getFirstChild();
974 while (child != null && child.getNodeType() != Node.ELEMENT_NODE) {
975 child = child.getNextSibling();
976 }
977
978 if (child != null) {
979 return (Element) child;
980 } else {
981 return null;
982 }
983 }
984
985
986
987
988
989
990
991 public static Element getNextSiblingElement(Node n) {
992 Node sib = n.getNextSibling();
993 while (sib != null && sib.getNodeType() != Node.ELEMENT_NODE) {
994 sib = sib.getNextSibling();
995 }
996
997 if (sib != null) {
998 return (Element) sib;
999 } else {
1000 return null;
1001 }
1002 }
1003
1004
1005
1006
1007
1008
1009
1010
1011 public static long durationToLong(String duration) {
1012 Duration xmlDuration = getDataTypeFactory().newDuration(duration);
1013 return xmlDuration.getTimeInMillis(new GregorianCalendar());
1014 }
1015
1016
1017
1018
1019
1020
1021
1022
1023 public static String longToDuration(long duration) {
1024 Duration xmlDuration = getDataTypeFactory().newDuration(duration);
1025 return xmlDuration.toString();
1026 }
1027 }