KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > springframework > util > xml > XmlValidationModeDetector


1 /*
2  * Copyright 2002-2007 the original author or authors.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16
17 package org.springframework.util.xml;
18
19 import java.io.BufferedReader JavaDoc;
20 import java.io.CharConversionException JavaDoc;
21 import java.io.IOException JavaDoc;
22 import java.io.InputStream JavaDoc;
23 import java.io.InputStreamReader JavaDoc;
24
25 import org.springframework.util.StringUtils;
26
27 /**
28  * Detects whether an XML stream is using DTD- or XSD-based validation.
29  *
30  * @author Rob Harrop
31  * @author Juergen Hoeller
32  * @since 2.0
33  */

34 public class XmlValidationModeDetector {
35
36     /**
37      * Indicates that the validation mode should be auto-guessed, since we cannot find
38      * a clear indication (probably choked on some special characters, or the like).
39      */

40     public static final int VALIDATION_AUTO = 1;
41
42     /**
43      * Indicates that DTD validation should be used (we found a "DOCTYPE" declaration).
44      */

45     public static final int VALIDATION_DTD = 2;
46
47     /**
48      * Indicates that XSD validation should be used (found no "DOCTYPE" declaration).
49      */

50     public static final int VALIDATION_XSD = 3;
51
52
53     /**
54      * The token in a XML document that declares the DTD to use for validation
55      * and thus that DTD validation is being used.
56      */

57     private static final String JavaDoc DOCTYPE = "DOCTYPE";
58
59     /**
60      * The token that indicates the start of an XML comment.
61      */

62     private static final String JavaDoc START_COMMENT = "<!--";
63
64     /**
65      * The token that indicates the end of an XML comment.
66      */

67     private static final String JavaDoc END_COMMENT = "-->";
68
69
70     /**
71      * Indicates whether or not the current parse position is inside an XML comment.
72      */

73     private boolean inComment;
74
75
76     /**
77      * Detect the validation mode for the XML document in the supplied {@link InputStream}.
78      * Note that the supplied {@link InputStream} is closed by this method before returning.
79      * @param inputStream the InputStream to parse
80      * @throws IOException in case of I/O failure
81      * @see #VALIDATION_DTD
82      * @see #VALIDATION_XSD
83      */

84     public int detectValidationMode(InputStream JavaDoc inputStream) throws IOException JavaDoc {
85         // Peek into the file to look for DOCTYPE.
86
BufferedReader JavaDoc reader = new BufferedReader JavaDoc(new InputStreamReader JavaDoc(inputStream));
87         try {
88             boolean isDtdValidated = false;
89             String JavaDoc content;
90             while ((content = reader.readLine()) != null) {
91                 content = consumeCommentTokens(content);
92                 if (this.inComment || !StringUtils.hasText(content)) {
93                     continue;
94                 }
95                 if (hasDoctype(content)) {
96                     isDtdValidated = true;
97                     break;
98                 }
99                 if (hasOpeningTag(content)) {
100                     // End of meaningful data...
101
break;
102                 }
103             }
104             return (isDtdValidated ? VALIDATION_DTD : VALIDATION_XSD);
105         }
106         catch (CharConversionException JavaDoc ex) {
107             // Choked on some character encoding...
108
// Leave the decision up to the caller.
109
return VALIDATION_AUTO;
110         }
111         finally {
112             reader.close();
113         }
114     }
115
116
117     /**
118      * Does the content contain the the DTD DOCTYPE declaration?
119      */

120     private boolean hasDoctype(String JavaDoc content) {
121         return (content.indexOf(DOCTYPE) > -1);
122     }
123
124     /**
125      * Does the supplied content contain an XML opening tag. If the parse state is currently
126      * in an XML comment then this method always returns false. It is expected that all comment
127      * tokens will have consumed for the supplied content before passing the remainder to this method.
128      */

129     private boolean hasOpeningTag(String JavaDoc content) {
130         if (this.inComment) {
131             return false;
132         }
133         int openTagIndex = content.indexOf('<');
134         return (openTagIndex > -1 && content.length() > openTagIndex && Character.isLetter(content.charAt(openTagIndex + 1)));
135     }
136
137     /**
138      * Consumes all the leading comment data in the given String and returns the remaining content, which
139      * may be empty since the supplied content might be all comment data. For our purposes it is only important
140      * to strip leading comment content on a line since the first piece of non comment content will be either
141      * the DOCTYPE declaration or the root element of the document.
142      */

143     private String JavaDoc consumeCommentTokens(String JavaDoc line) {
144         if (line.indexOf(START_COMMENT) == -1 && line.indexOf(END_COMMENT) == -1) {
145             return line;
146         }
147         while ((line = consume(line)) != null) {
148             if (!this.inComment && !line.trim().startsWith(START_COMMENT)) {
149                 return line;
150             }
151         }
152         return line;
153     }
154
155     /**
156      * Consume the next comment token, update the "inComment" flag
157      * and return the remaining content.
158      */

159     private String JavaDoc consume(String JavaDoc line) {
160         int index = (this.inComment ? endComment(line) : startComment(line));
161         return (index == -1 ? null : line.substring(index));
162     }
163
164     /**
165      * Try to consume the {@link #START_COMMENT} token.
166      * @see #commentToken(String, String, boolean)
167      */

168     private int startComment(String JavaDoc line) {
169         return commentToken(line, START_COMMENT, true);
170     }
171
172     private int endComment(String JavaDoc line) {
173         return commentToken(line, END_COMMENT, false);
174     }
175
176     /**
177      * Try to consume the supplied token against the supplied content and update the
178      * in comment parse state to the supplied value. Returns the index into the content
179      * which is after the token or -1 if the token is not found.
180      */

181     private int commentToken(String JavaDoc line, String JavaDoc token, boolean inCommentIfPresent) {
182         int index = line.indexOf(token);
183         if (index > - 1) {
184             this.inComment = inCommentIfPresent;
185         }
186         return (index == -1 ? index : index + token.length());
187     }
188
189 }
190
Popular Tags