1
/* Copyright 2002-2005 Elliotte Rusty Harold
3
This library is free software; you can redistribute it and/or modify
4
it under the terms of version 2.1 of the GNU Lesser General Public
5
License as published by the Free Software Foundation.
7
This library is distributed in the hope that it will be useful,
8
but WITHOUT ANY WARRANTY; without even the implied warranty of
9
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10
GNU Lesser General Public License for more details.
12
You should have received a copy of the GNU Lesser General Public
13
License along with this library; if not, write to the
14
Free Software Foundation, Inc., 59 Temple Place, Suite 330,
15
Boston, MA 02111-1307 USA
17
You can contact Elliotte Rusty Harold by sending e-mail to
18
elharo@metalab.unc.edu. Please include the word "XOM" in the
19
subject line. The XOM home page is located at http://www.xom.nu/
22
package nu.xom.xinclude;
24
import java.io.BufferedInputStream;
25
import java.io.BufferedReader;
26
import java.io.IOException;
27
import java.io.InputStream;
28
import java.io.InputStreamReader;
29
import java.io.Reader;
30
import java.io.UnsupportedEncodingException;
31
import java.net.MalformedURLException;
33
import java.net.URLConnection;
34
import java.util.Locale;
35
import java.util.ArrayList;
37
import nu.xom.Attribute;
38
import nu.xom.Builder;
39
import nu.xom.DocType;
40
import nu.xom.Document;
41
import nu.xom.Element;
42
import nu.xom.Elements;
43
import nu.xom.MalformedURIException;
45
import nu.xom.NodeFactory;
47
import nu.xom.ParentNode;
48
import nu.xom.ParsingException;
53
* Implements XInclude resolution as specified in
54
* <a href="http://www.w3.org/TR/2004/REC-xinclude-20041220/"
55
* target="_top"><cite>XML Inclusions (XInclude) Version
56
* 1.0</cite></a>. Fallbacks are supported.
57
* The XPointer <code>element()</code> scheme and
58
* shorthand XPointers are also supported. The XPointer
59
* <code>xpointer()</code> scheme is not supported.
60
* The <code>accept</code> and <code>accept-language</code>
61
* attributes are supported.
64
* @author Elliotte Rusty Harold
68
public class XIncluder {
70
private static String version = System.getProperty("java.version");
72
// could rewrite this to handle only elements in documents
73
// (no parentless elements) and then add code to handle Nodes
74
// and parentless elements by sticking each one in a Document
76
// prevent instantiation
77
private XIncluder() {}
81
* The namespace name of all XInclude elements.
84
public final static String XINCLUDE_NS
85
= "http://www.w3.org/2001/XInclude";
89
* Returns a copy of the document in which all
90
* <code>xinclude:include</code> elements have been
91
* replaced by their referenced content. The original
92
* <code>Document</code> object is not modified.
93
* Resolution is recursive; that is, include elements
94
* in the included documents are themselves resolved.
95
* The <code>Document</code> returned contains no
99
* @param in the document in which include elements
102
* @return copy of the document in which
103
* all <code>xinclude:include</code> elements
104
* have been replaced by their referenced content
106
* @throws BadParseAttributeException if an <code>include</code>
107
* element has a <code>parse</code> attribute with any value
108
* other than <code>text</code> or <code>parse</code>
109
* @throws InclusionLoopException if the document
110
* contains an XInclude element that attempts to include
111
* a document in which this element is directly or indirectly
113
* @throws IOException if an included document could not be loaded,
114
* and no fallback was available
115
* @throws NoIncludeLocationException if an <code>xinclude:include</code>
116
* element does not have an <code>href</code> attribute
117
* @throws ParsingException if an included XML document
119
* @throws UnsupportedEncodingException if an included document
120
* used an encoding this parser does not support, and no
121
* fallback was available
122
* @throws XIncludeException if the document violates the
123
* syntax rules of XInclude
124
* @throws XMLException if resolving an include element would
125
* result in a malformed document
127
public static Document resolve(Document in)
128
throws BadParseAttributeException, InclusionLoopException,
129
IOException, NoIncludeLocationException, ParsingException,
130
UnsupportedEncodingException, XIncludeException {
132
Builder builder = new Builder();
133
return resolve(in, builder);
139
* Returns a copy of the document in which all
140
* <code>xinclude:include</code> elements have been
141
* replaced by their referenced content as loaded by the builder.
142
* The original <code>Document</code> object is not modified.
143
* Resolution is recursive; that is, include elements
144
* in the included documents are themselves resolved.
145
* The document returned contains no <code>include</code> elements.
148
* @param in the document in which include elements
150
* @param builder the builder used to build the
151
* nodes included from other documents
153
* @return copy of the document in which
154
* all <code>xinclude:include</code> elements
155
* have been replaced by their referenced content
157
* @throws BadParseAttributeException if an <code>include</code>
158
* element has a <code>parse</code> attribute with any value
159
* other than <code>text</code> or <code>parse</code>
160
* @throws InclusionLoopException if the document
161
* contains an XInclude element that attempts to include
162
* a document in which this element is directly or indirectly
164
* @throws IOException if an included document could not be loaded,
165
* and no fallback was available
166
* @throws NoIncludeLocationException if an <code>xinclude:include</code>
167
* element does not have an href attribute.
168
* @throws ParsingException if an included XML document
170
* @throws UnsupportedEncodingException if an included document
171
* used an encoding this parser does not support, and no
172
* fallback was available
173
* @throws XIncludeException if the document violates the
174
* syntax rules of XInclude
175
* @throws XMLException if resolving an include element would
176
* result in a malformed document
178
public static Document resolve(Document in, Builder builder)
179
throws BadParseAttributeException, InclusionLoopException,
180
IOException, NoIncludeLocationException, ParsingException,
181
UnsupportedEncodingException, XIncludeException {
183
Document copy = new Document(in);
184
resolveInPlace(copy, builder);
191
* Modifies a document by replacing all
192
* <code>xinclude:include</code> elements
193
* by their referenced content.
194
* Resolution is recursive; that is, include elements
195
* in the included documents are themselves resolved.
196
* The resolved document contains no
197
* <code>xinclude:include</code> elements.
201
* If the inclusion fails for any reason—XInclude syntax
202
* error, missing resource with no fallback, etc.—the document
203
* may be left in a partially resolved state.
206
* @param in the document in which include elements
209
* @throws BadParseAttributeException if an <code>include</code>
210
* element has a <code>parse</code> attribute
211
* with any value other than <code>text</code>
212
* or <code>parse</code>
213
* @throws InclusionLoopException if the document
214
* contains an XInclude element that attempts to include a
215
* document in which this element is directly or indirectly
217
* @throws IOException if an included document could not be loaded,
218
* and no fallback was available
219
* @throws NoIncludeLocationException if an <code>xinclude:include</code>
220
* element does not have an <code>href</code> attribute
221
* @throws ParsingException if an included XML document
223
* @throws UnsupportedEncodingException if an included document
224
* used an encoding this parser does not support, and no
225
* fallback was available
226
* @throws XIncludeException if the document violates the
227
* syntax rules of XInclude
228
* @throws XMLException if resolving an include element would
229
* result in a malformed document
231
public static void resolveInPlace(Document in)
232
throws BadParseAttributeException, InclusionLoopException,
233
IOException, NoIncludeLocationException, ParsingException,
234
UnsupportedEncodingException, XIncludeException {
235
resolveInPlace(in, new Builder());
240
* Modifies a document by replacing all
241
* <code>xinclude:include</code> elements with their referenced
242
* content as loaded by the builder. Resolution is recursive;
243
* that is, <code>include</code> elements in the included documents
244
* are themselves resolved. The resolved document contains no
245
* <code>xinclude:include</code> elements.
249
* If the inclusion fails for any reason — XInclude syntax
250
* error, missing resource with no fallback, etc. — the
251
* document may be left in a partially resolved state.
254
* @param in the document in which include elements
256
* @param builder the builder used to build the
257
* nodes included from other documents
259
* @throws BadParseAttributeException if an <code>include</code>
260
* element has a <code>parse</code> attribute
261
* with any value other than <code>text</code>
262
* or <code>parse</code>
263
* @throws InclusionLoopException if this element
264
* contains an XInclude element that attempts to include a
265
* document in which this element is directly or indirectly
267
* @throws IOException if an included document could not be loaded,
268
* and no fallback was available
269
* @throws NoIncludeLocationException if an <code>xinclude:include</code>
270
* element does not have an <code>href</code> attribute.
271
* @throws ParsingException if an included XML document
273
* @throws UnsupportedEncodingException if an included document
274
* used an encoding this parser does not support, and no
275
* fallback was available
276
* @throws XIncludeException if the document violates the
277
* syntax rules of XInclude
278
* @throws XMLException if resolving an include element would
279
* result in a malformed document
281
public static void resolveInPlace(Document in, Builder builder)
282
throws BadParseAttributeException, InclusionLoopException,
283
IOException, NoIncludeLocationException, ParsingException,
284
UnsupportedEncodingException, XIncludeException {
286
ArrayList stack = new ArrayList();
287
resolveInPlace(in, builder, stack);
292
private static void resolveInPlace(
293
Document in, Builder builder, ArrayList baseURLs)
294
throws IOException, ParsingException, XIncludeException {
296
String base = in.getBaseURI();
297
// workaround a bug in Sun VMs
298
if (base != null && base.startsWith("file:///")) {
299
base = "file:/" + base.substring(8);
303
Element root = in.getRootElement();
304
resolve(root, builder, baseURLs);
305
baseURLs.remove(baseURLs.size()-1);
310
private static void resolve(
311
Element element, Builder builder, ArrayList baseURLs)
312
throws IOException, ParsingException, XIncludeException {
314
resolve(element, builder, baseURLs, null);
319
private static void resolve(
320
Element element, Builder builder, ArrayList baseURLs, Document originalDoc)
321
throws IOException, ParsingException, XIncludeException {
323
if (isIncludeElement(element)) {
324
verifyIncludeElement(element);
326
String parse = element.getAttributeValue("parse");
327
if (parse == null) parse = "xml";
328
String xpointer = element.getAttributeValue("xpointer");
329
String encoding = element.getAttributeValue("encoding");
330
String href = element.getAttributeValue("href");
331
// empty string href is same as no href attribute
332
if ("".equals(href)) href = null;
334
ParentNode parent = element.getParent();
335
String base = element.getBaseURI();
338
baseURL = new URL(base);
340
catch (MalformedURLException ex) {
345
// xml:base attributes added to maintain the
346
// base URI should not have fragment IDs
348
if (baseURL != null && href != null) {
349
url = absolutize(baseURL, href);
351
else if (href != null) {
356
catch (MalformedURIException ex) {
357
if (baseURL == null) {
358
throw new BadHrefAttributeException(
359
"Could not resolve relative URI " + href
360
+ " because the xi:include element does"
361
+ " not have a base URI.", href);
363
throw new BadHrefAttributeException("Illegal IRI in href attribute", href);
367
String accept = element.getAttributeValue("accept");
369
String acceptLanguage = element.getAttributeValue("accept-language");
370
checkHeader(acceptLanguage);
372
if (parse.equals("xml")) {
374
String parentLanguage = "";
375
if (parent instanceof Element) {
376
parentLanguage = getXMLLangValue((Element) parent);
381
replacements = downloadXMLDocument(url,
382
xpointer, builder, baseURLs, accept, acceptLanguage, parentLanguage);
383
// Add base URIs. Base URIs added by XInclusion require
384
// the element to maintain the same base URI as it had
385
// in the original document. Since its base URI in the
386
// original document does not contain a fragment ID,
387
// therefore its base URI after inclusion shouldn't,
388
// and this special case is unnecessary. Base URI fixup
389
// should not add the fragment ID.
390
for (int i = 0; i < replacements.size(); i++) {
391
Node child = replacements.get(i);
392
if (child instanceof Element) {
393
String noFragment = child.getBaseURI();
394
if (noFragment.indexOf('#') >= 0) {
395
noFragment = noFragment.substring(
396
0, noFragment.indexOf('#'));
398
Element baseless = (Element) child;
400
// parent is null here; need to get real parent
401
String parentBase = parent.getBaseURI();
402
if (parentBase != null && ! "".equals(parentBase)) {
403
parentBase = getDirectoryBase(parentBase);
406
if (noFragment.startsWith(parentBase)) {
407
noFragment = noFragment.substring(parentBase.length());
409
Attribute baseAttribute = new Attribute(
411
"http://www.w3.org/XML/1998/namespace",
414
baseless.addAttribute(baseAttribute);
420
Document parentDoc = element.getDocument();
421
if (parentDoc == null) {
422
parentDoc = originalDoc;
424
Nodes originals = XPointer.query(parentDoc, xpointer);
425
replacements = new Nodes();
426
for (int i = 0; i < originals.size(); i++) {
427
Node original = originals.get(i);
428
// current implementation of XPointer never returns non-elements
429
if (contains((Element) original, element)) {
430
throw new InclusionLoopException(
431
"Element tried to include itself"
434
Node copy = original.copy();
435
replacements.append(copy);
437
replacements = resolveXPointerSelection(
438
replacements, builder, baseURLs, parentDoc);
442
// Will fail if we're replacing the root element with
443
// a node list containing zero or multiple elements,
444
// but that should fail. However, I may wish to
445
// adjust the type of exception thrown. This is only
446
// relevant if I add support for the xpointer scheme
447
// since otherwise you can only point at one element
449
if (parent instanceof Element) {
450
int position = parent.indexOf(element);
451
for (int i = 0; i < replacements.size(); i++) {
452
Node child = replacements.get(i);
453
parent.insertChild(child, position+i);
457
else { // root element needs special treatment
458
// I am assuming here that it is not possible
459
// for parent to be null. I think this is true
460
// in the current version, but it could change
461
// if I made it possible to directly resolve an
462
// element or a Nodes.
463
Document doc = (Document) parent;
467
Node child = replacements.get(i);
469
if (child instanceof Element) {
470
doc.setRootElement((Element) child);
475
child, doc.indexOf(element)
481
Element root = doc.getRootElement();
482
int position = doc.indexOf(root);
483
for (int j=i; j < replacements.size(); j++) {
485
replacements.get(j), position+1+j-i
490
else if (parse.equals("text")) {
492
= downloadTextDocument(url, encoding, builder, accept, acceptLanguage);
493
for (int j = 0; j < replacements.size(); j++) {
494
Node replacement = replacements.get(j);
495
if (replacement instanceof Attribute) {
496
((Element) parent).addAttribute((Attribute) replacement);
499
parent.insertChild(replacement, parent.indexOf(element));
502
parent.removeChild(element);
505
throw new BadParseAttributeException(
506
"Bad value for parse attribute: " + parse,
507
element.getDocument().getBaseURI());
511
catch (IOException ex) {
512
processFallback(element, builder, baseURLs, parent, ex);
514
catch (XPointerSyntaxException ex) {
515
processFallback(element, builder, baseURLs, parent, ex);
517
catch (XPointerResourceException ex) {
518
// Process fallbacks; I'm not sure this is correct
519
// behavior. Possibly this should include nothing. See
520
// http://lists.w3.org/Archives/Public/www-xml-xinclude-comments/2003Aug/0000.html
521
// Daniel Veillard thinks this is correct. See
522
// http://lists.w3.org/Archives/Public/www-xml-xinclude-comments/2003Aug/0001.html
523
processFallback(element, builder, baseURLs, parent, ex);
527
else if (isFallbackElement(element)) {
528
throw new MisplacedFallbackException(
529
"Fallback element outside include element",
530
element.getDocument().getBaseURI()
534
Elements children = element.getChildElements();
535
for (int i = 0; i < children.size(); i++) {
536
resolve(children.get(i), builder, baseURLs);
543
// ???? Move this into URIUtil when it goes public
544
private static String getDirectoryBase(String parentBase) {
545
if (parentBase.endsWith("/")) return parentBase;
546
int lastSlash = parentBase.lastIndexOf('/');
547
return parentBase.substring(0, lastSlash+1);
552
private static void verifyIncludeElement(Element element)
553
throws XIncludeException {
556
testForFragmentIdentifier(element);
557
verifyEncoding(element);
558
testForForbiddenChildElements(element);
562
private static void testHref(Element include) throws NoIncludeLocationException {
564
String href = include.getAttributeValue("href");
565
String xpointer = include.getAttributeValue("xpointer");
566
if (href == null && xpointer == null) {
567
throw new NoIncludeLocationException(
568
"Missing href attribute",
569
include.getDocument().getBaseURI()
575
private static void testForFragmentIdentifier(Element include)
576
throws BadHrefAttributeException {
578
String href = include.getAttributeValue("href");
580
if (href.indexOf('#') > -1) {
581
throw new BadHrefAttributeException(
582
"fragment identifier in URI " + href, include.getBaseURI()
590
private static void verifyEncoding(Element include)
591
throws BadEncodingAttributeException {
593
String encoding = include.getAttributeValue("encoding");
594
if (encoding == null) return;
595
// production 81 of XML spec
596
// EncName :=[A-Za-z] ([A-Za-z0-9._] | '-')*
597
char[] text = encoding.toCharArray();
598
if (text.length == 0) {
599
throw new BadEncodingAttributeException(
600
"Empty encoding attribute", include.getBaseURI());
603
if (!((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'))) {
604
throw new BadEncodingAttributeException(
605
"Illegal value for encoding attribute: " + encoding, include.getBaseURI()
608
for (int i = 1; i < text.length; i++) {
610
if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')
611
|| (c >= '0' && c <= '9') || c == '-' || c == '_' || c == '.') {
614
throw new BadEncodingAttributeException(
615
"Illegal value for encoding attribute: " + encoding, include.getBaseURI()
622
// hack because URIUtil isn't public
623
private static URL absolutize(URL baseURL, String href)
624
throws MalformedURLException, BadHrefAttributeException {
626
Element parent = new Element("c");
627
parent.setBaseURI(baseURL.toExternalForm());
628
Element child = new Element("c");
629
parent.appendChild(child);
630
child.addAttribute(new Attribute(
631
"xml:base", "http://www.w3.org/XML/1998/namespace", href));
632
URL result = new URL(child.getBaseURI());
633
if (!"".equals(href) && result.equals(baseURL)) {
634
if (! baseURL.toExternalForm().endsWith(href)) {
635
throw new BadHrefAttributeException(href
636
+ " is not a syntactically correct IRI");
644
private static void testURISyntax(String href) {
645
Element e = new Element("e");
646
e.setNamespaceURI(href);
650
private static String getXMLLangValue(Element element) {
653
Attribute lang = element.getAttribute(
654
"lang", "http://www.w3.org/XML/1998/namespace");
655
if (lang != null) return lang.getValue();
656
ParentNode parent = element.getParent();
657
if (parent == null) return "";
658
else if (parent instanceof Document) return "";
659
else element = (Element) parent;
665
// This assumes current implementation of XPointer that
666
// always selects exactly one element or throws an exception.
667
private static Nodes resolveXPointerSelection(Nodes in,
668
Builder builder, ArrayList baseURLs, Document original)
669
throws IOException, ParsingException, XIncludeException {
671
Element preinclude = (Element) in.get(0);
672
return resolveSilently(preinclude, builder, baseURLs, original);
677
private static boolean contains(ParentNode ancestor, Node descendant) {
679
for (Node parent = descendant;
681
parent=parent.getParent()) {
682
if (parent == ancestor) return true;
690
private static Nodes resolveSilently(
691
Element element, Builder builder, ArrayList baseURLs, Document originalDoc)
692
throws IOException, ParsingException, XIncludeException {
694
// There is no possibility the element passed to this method
695
// is an include or a fallback element
696
if (isIncludeElement(element) || isFallbackElement(element) ) {
697
throw new RuntimeException(
698
"XOM BUG: include or fallback element passed to resolveSilently;"
699
+ " please report with a test case");
702
Elements children = element.getChildElements();
703
for (int i = 0; i < children.size(); i++) {
704
resolve(children.get(i), builder, baseURLs, originalDoc);
706
return new Nodes(element);
711
private static void testForForbiddenChildElements(Element element)
712
throws XIncludeException {
715
Elements children = element.getChildElements();
716
int size = children.size();
717
for (int i = 0; i < size; i++) {
718
Element child = children.get(i);
719
if (XINCLUDE_NS.equals(child.getNamespaceURI())) {
720
if ("fallback".equals(child.getLocalName())) {
723
throw new XIncludeException("Multiple fallback elements",
724
element.getDocument().getBaseURI());
728
throw new XIncludeException(
729
"Include element contains an include child",
730
element.getDocument().getBaseURI());
738
private static void processFallback(Element includeElement,
739
Builder builder, ArrayList baseURLs, ParentNode parent, Exception ex)
740
throws XIncludeException, IOException, ParsingException {
743
= includeElement.getFirstChildElement("fallback", XINCLUDE_NS);
744
if (fallback == null) {
745
if (ex instanceof IOException) throw (IOException) ex;
746
XIncludeException ex2 = new XIncludeException(
747
ex.getMessage(), includeElement.getDocument().getBaseURI());
752
while (fallback.getChildCount() > 0) {
753
Node child = fallback.getChild(0);
754
if (child instanceof Element) {
755
resolve((Element) child, builder, baseURLs);
757
child = fallback.getChild(0);
759
parent.insertChild(child, parent.indexOf(includeElement));
761
includeElement.detach();
766
// I could probably move the xpointer out of this method
767
private static Nodes downloadXMLDocument(
768
URL source, String xpointer, Builder builder, ArrayList baseURLs,
769
String accept, String acceptLanguage, String parentLanguage)
770
throws IOException, ParsingException, XIncludeException,
771
XPointerSyntaxException, XPointerResourceException {
773
String base = source.toExternalForm();
774
if (xpointer == null && baseURLs.indexOf(base) != -1) {
775
throw new InclusionLoopException(
776
"Tried to include the already included document " + base +
777
" from " + baseURLs.get(baseURLs.size()-1), (String) baseURLs.get(baseURLs.size()-1));
780
URLConnection uc = source.openConnection();
781
setHeaders(uc, accept, acceptLanguage);
782
InputStream in = new BufferedInputStream(uc.getInputStream());
785
doc = builder.build(in, source.toExternalForm());
791
resolveInPlace(doc, builder, baseURLs);
793
if (xpointer != null && xpointer.length() != 0) {
794
included = XPointer.query(doc, xpointer);
795
// fill in lang attributes here
796
for (int i = 0; i < included.size(); i++) {
797
Node node = included.get(i);
798
// Current implementation can only select elements
799
Element top = (Element) node;
800
Attribute lang = top.getAttribute("lang",
801
"http://www.w3.org/XML/1998/namespace");
803
String childLanguage = getXMLLangValue(top);
804
if (!parentLanguage.equals(childLanguage)) {
805
top.addAttribute(new Attribute("xml:lang",
806
"http://www.w3.org/XML/1998/namespace",
813
included = new Nodes();
814
for (int i = 0; i < doc.getChildCount(); i++) {
815
Node child = doc.getChild(i);
816
if (!(child instanceof DocType)) {
817
included.append(child);
821
// so we can detach the old root if necessary
822
doc.setRootElement(new Element("f"));
823
for (int i = 0; i < included.size(); i++) {
824
Node node = included.get(i);
825
// Take account of xml:base attribute, which we normally
826
// don't do when detaching
827
String noFragment = node.getBaseURI();
828
if (noFragment.indexOf('#') >= 0) {
829
noFragment = noFragment.substring(0, noFragment.indexOf('#'));
832
if (node instanceof Element) {
833
((Element) node).setBaseURI(noFragment);
844
* This utility method reads a document at a specified URL
845
* and returns the contents of that document as a <code>Text</code>.
846
* It's used to include files with <code>parse="text"</code>.
849
* @param source <code>URL</code> of the document to download
850
* @param encoding encoding of the document; e.g. UTF-8,
852
* @param builder the <code>Builder</code> used to build the
853
* nodes included from other documents
855
* @return the document retrieved from the source <code>URL</code>
857
* @throws IOException if the remote document cannot
858
* be read due to an I/O error
860
private static Nodes downloadTextDocument(
861
URL source, String encoding, Builder builder,
862
String accept, String language)
863
throws IOException, XIncludeException {
865
if (encoding == null || encoding.length() == 0) {
869
URLConnection uc = source.openConnection();
870
setHeaders(uc, accept, language);
872
String encodingFromHeader = uc.getContentEncoding();
873
String contentType = uc.getContentType();
874
int contentLength = uc.getContentLength();
875
if (contentLength < 0) contentLength = 1024;
876
InputStream in = new BufferedInputStream(uc.getInputStream());
878
if (encodingFromHeader != null) encoding = encodingFromHeader;
880
if (contentType != null) {
881
contentType = contentType.toLowerCase(Locale.ENGLISH);
882
if (contentType.equals("text/xml")
883
|| contentType.equals("application/xml")
884
|| (contentType.startsWith("text/")
885
&& contentType.endsWith("+xml") )
886
|| (contentType.startsWith("application/")
887
&& contentType.endsWith("+xml"))) {
889
= EncodingHeuristics.readEncodingFromStream(in);
893
// workaround for pre-1.3 VMs that don't recognize UTF-16
894
if (version.startsWith("1.2") || version.startsWith("1.1")) {
895
if (encoding.equalsIgnoreCase("UTF-16")) {
896
// is it big-endian or little-endian?
898
int first = in.read();
899
if (first == 0xFF) encoding = "UnicodeLittle";
900
else encoding="UnicodeBig";
903
else if (encoding.equalsIgnoreCase("UnicodeBigUnmarked")) {
904
encoding = "UnicodeBig";
906
else if (encoding.equalsIgnoreCase("UnicodeLittleUnmarked")) {
907
encoding = "UnicodeLittle";
910
Reader reader = new BufferedReader(
911
new InputStreamReader(in, encoding)
913
StringBuffer sb = new StringBuffer(contentLength);
914
for (int c = reader.read(); c != -1; c = reader.read()) {
918
NodeFactory factory = builder.getNodeFactory();
919
if (factory != null) {
920
return factory.makeText(sb.toString());
922
else return new Nodes(new Text(sb.toString()));
931
private static void setHeaders(URLConnection uc, String accept,
932
String language) throws BadHTTPHeaderException {
934
if (accept != null) {
936
uc.setRequestProperty("accept", accept);
938
if (language != null) {
939
checkHeader(language);
940
uc.setRequestProperty("accept-language", language);
946
private static void checkHeader(String header)
947
throws BadHTTPHeaderException {
949
if (header == null) return;
950
int length = header.length();
951
for (int i = 0; i < length; i++) {
952
char c = header.charAt(i);
953
if (c < 0x20 || c > 0x7E) {
954
throw new BadHTTPHeaderException(
955
"Header contains illegal character 0x"
956
+ Integer.toHexString(c).toUpperCase());
963
private static boolean isIncludeElement(Element element) {
965
return element.getLocalName().equals("include")
966
&& element.getNamespaceURI().equals(XINCLUDE_NS);
971
private static boolean isFallbackElement(Element element) {
973
return element.getLocalName().equals("fallback")
974
&& element.getNamespaceURI().equals(XINCLUDE_NS);
b'\\ No newline at end of file'