1 /*
2 * reserved comment block
3 * DO NOT REMOVE OR ALTER!
4 */
5 /*
6 * The Apache Software License, Version 1.1
7 *
8 *
9 * Copyright (c) 1999-2003 The Apache Software Foundation.
10 * All rights reserved.
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 *
16 * 1. Redistributions of source code must retain the above copyright
17 * notice, this list of conditions and the following disclaimer.
18 *
19 * 2. Redistributions in binary form must reproduce the above copyright
20 * notice, this list of conditions and the following disclaimer in
21 * the documentation and/or other materials provided with the
22 * distribution.
23 *
24 * 3. The end-user documentation included with the redistribution,
25 * if any, must include the following acknowledgment:
26 * "This product includes software developed by the
27 * Apache Software Foundation (http://www.apache.org/)."
28 * Alternately, this acknowledgment may appear in the software itself,
29 * if and wherever such third-party acknowledgments normally appear.
30 *
31 * 4. The names "Xerces" and "Apache Software Foundation" must
32 * not be used to endorse or promote products derived from this
33 * software without prior written permission. For written
34 * permission, please contact apache@apache.org.
35 *
36 * 5. Products derived from this software may not be called "Apache",
37 * nor may "Apache" appear in their name, without prior written
38 * permission of the Apache Software Foundation.
39 *
40 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
41 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
42 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
43 * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
44 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
45 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
46 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
47 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
48 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
49 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
50 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
51 * SUCH DAMAGE.
52 * ====================================================================
53 *
54 * This software consists of voluntary contributions made by many
55 * individuals on behalf of the Apache Software Foundation and was
56 * originally based on software copyright (c) 2003, International
57 * Business Machines, Inc., http://www.apache.org. For more
58 * information on the Apache Software Foundation, please see
59 * <http://www.apache.org/>.
60 */
61
62 package com.sun.org.apache.xerces.internal.impl;
63
64 import java.io.EOFException;
65 import java.io.IOException;
66
67 import com.sun.org.apache.xerces.internal.impl.msg.XMLMessageFormatter;
68 import com.sun.org.apache.xerces.internal.util.SymbolTable;
69 import com.sun.org.apache.xerces.internal.xni.XMLString;
70 import com.sun.org.apache.xerces.internal.xni.parser.XMLComponentManager;
71 import com.sun.org.apache.xerces.internal.xni.parser.XMLConfigurationException;
72 import com.sun.org.apache.xerces.internal.xni.parser.XMLInputSource;
73 import com.sun.xml.internal.stream.Entity.ScannedEntity;
74
75 /**
76 * This class scans the version of the document to determine
77 * which scanner to use: XML 1.1 or XML 1.0.
78 * The version is scanned using XML 1.1. scanner.
79 *
80 * @xerces.internal
81 *
82 * @author Neil Graham, IBM
83 * @author Elena Litani, IBM
84 */
85 public class XMLVersionDetector {
86
87 //
88 // Constants
89 //
90
91 private final static char[] XML11_VERSION = new char[]{'1', '.', '1'};
92
93
94 // property identifiers
95
96 /** Property identifier: symbol table. */
97 protected static final String SYMBOL_TABLE =
98 Constants.XERCES_PROPERTY_PREFIX + Constants.SYMBOL_TABLE_PROPERTY;
99
100 /** Property identifier: error reporter. */
101 protected static final String ERROR_REPORTER =
102 Constants.XERCES_PROPERTY_PREFIX + Constants.ERROR_REPORTER_PROPERTY;
103
104 /** Property identifier: entity manager. */
105 protected static final String ENTITY_MANAGER =
106 Constants.XERCES_PROPERTY_PREFIX + Constants.ENTITY_MANAGER_PROPERTY;
107
108 //
109 // Data
110 //
111
112 /** Symbol: "version". */
113 protected final static String fVersionSymbol = "version".intern();
114
115 // symbol: [xml]:
116 protected static final String fXMLSymbol = "[xml]".intern();
117
118 /** Symbol table. */
119 protected SymbolTable fSymbolTable;
120
121 /** Error reporter. */
122 protected XMLErrorReporter fErrorReporter;
123
124 /** Entity manager. */
125 protected XMLEntityManager fEntityManager;
126
127 protected String fEncoding = null;
128
129 private XMLString fVersionNum = new XMLString();
130
131 private final char [] fExpectedVersionString = {'<', '?', 'x', 'm', 'l', ' ', 'v', 'e', 'r', 's',
132 'i', 'o', 'n', '=', ' ', ' ', ' ', ' ', ' '};
133
134 /**
135 *
136 *
137 * @param componentManager The component manager.
138 *
139 * @throws SAXException Throws exception if required features and
140 * properties cannot be found.
141 */
142 public void reset(XMLComponentManager componentManager)
143 throws XMLConfigurationException {
144
145 // Xerces properties
146 fSymbolTable = (SymbolTable)componentManager.getProperty(SYMBOL_TABLE);
147 fErrorReporter = (XMLErrorReporter)componentManager.getProperty(ERROR_REPORTER);
148 fEntityManager = (XMLEntityManager)componentManager.getProperty(ENTITY_MANAGER);
149 for(int i=14; i<fExpectedVersionString.length; i++ )
150 fExpectedVersionString[i] = ' ';
151 } // reset(XMLComponentManager)
152
153 /**
154 * Reset the reference to the appropriate scanner given the version of the
155 * document and start document scanning.
156 * @param scanner - the scanner to use
157 * @param version - the version of the document (XML 1.1 or XML 1.0).
158 */
159 public void startDocumentParsing(XMLEntityHandler scanner, short version){
160
161 if (version == Constants.XML_VERSION_1_0){
162 fEntityManager.setScannerVersion(Constants.XML_VERSION_1_0);
163 }
164 else {
165 fEntityManager.setScannerVersion(Constants.XML_VERSION_1_1);
166 }
167 // Make sure the locator used by the error reporter is the current entity scanner.
168 fErrorReporter.setDocumentLocator(fEntityManager.getEntityScanner());
169
170 // Note: above we reset fEntityScanner in the entity manager, thus in startEntity
171 // in each scanner fEntityScanner field must be reset to reflect the change.
172 //
173 fEntityManager.setEntityHandler(scanner);
174
175 scanner.startEntity(fXMLSymbol, fEntityManager.getCurrentResourceIdentifier(), fEncoding, null);
176 }
177
178
179 /**
180 * This methods scans the XML declaration to find out the version
181 * (and provisional encoding) of the document.
182 * The scanning is doing using XML 1.1 scanner.
183 * @param inputSource
184 * @return short - Constants.XML_VERSION_1_1 if document version 1.1,
185 * otherwise Constants.XML_VERSION_1_0
186 * @throws IOException
187 */
188 public short determineDocVersion(XMLInputSource inputSource) throws IOException {
189 fEncoding = fEntityManager.setupCurrentEntity(fXMLSymbol, inputSource, false, true);
190
191 // Must use XML 1.0 scanner to handle whitespace correctly
192 // in the XML declaration.
193 fEntityManager.setScannerVersion(Constants.XML_VERSION_1_0);
194 XMLEntityScanner scanner = fEntityManager.getEntityScanner();
195 try {
196 if (!scanner.skipString("<?xml")) {
197 // definitely not a well-formed 1.1 doc!
198 return Constants.XML_VERSION_1_0;
199 }
200 if (!scanner.skipDeclSpaces()) {
201 fixupCurrentEntity(fEntityManager, fExpectedVersionString, 5);
202 return Constants.XML_VERSION_1_0;
203 }
204 if (!scanner.skipString("version")) {
205 fixupCurrentEntity(fEntityManager, fExpectedVersionString, 6);
206 return Constants.XML_VERSION_1_0;
207 }
208 scanner.skipDeclSpaces();
209 // Check if the next character is '='. If it is then consume it.
210 if (scanner.peekChar() != '=') {
211 fixupCurrentEntity(fEntityManager, fExpectedVersionString, 13);
212 return Constants.XML_VERSION_1_0;
213 }
214 scanner.scanChar();
215 scanner.skipDeclSpaces();
216 int quoteChar = scanner.scanChar();
217 fExpectedVersionString[14] = (char) quoteChar;
218 for (int versionPos = 0; versionPos < XML11_VERSION.length; versionPos++) {
219 fExpectedVersionString[15 + versionPos] = (char) scanner.scanChar();
220 }
221 // REVISIT: should we check whether this equals quoteChar?
222 fExpectedVersionString[18] = (char) scanner.scanChar();
223 fixupCurrentEntity(fEntityManager, fExpectedVersionString, 19);
224 int matched = 0;
225 for (; matched < XML11_VERSION.length; matched++) {
226 if (fExpectedVersionString[15 + matched] != XML11_VERSION[matched])
227 break;
228 }
229 if (matched == XML11_VERSION.length)
230 return Constants.XML_VERSION_1_1;
231 return Constants.XML_VERSION_1_0;
232 // premature end of file
233 }
234 catch (EOFException e) {
235 fErrorReporter.reportError(
236 XMLMessageFormatter.XML_DOMAIN,
237 "PrematureEOF",
238 null,
239 XMLErrorReporter.SEVERITY_FATAL_ERROR);
240 return Constants.XML_VERSION_1_0;
241
242 }
243
244 }
245
246 // This method prepends "length" chars from the char array,
247 // from offset 0, to the manager's fCurrentEntity.ch.
248 private void fixupCurrentEntity(XMLEntityManager manager,
249 char [] scannedChars, int length) {
250 ScannedEntity currentEntity = manager.getCurrentEntity();
251 if(currentEntity.count-currentEntity.position+length > currentEntity.ch.length) {
252 //resize array; this case is hard to imagine...
253 char[] tempCh = currentEntity.ch;
254 currentEntity.ch = new char[length+currentEntity.count-currentEntity.position+1];
255 System.arraycopy(tempCh, 0, currentEntity.ch, 0, tempCh.length);
256 }
257 if(currentEntity.position < length) {
258 // have to move sensitive stuff out of the way...
259 System.arraycopy(currentEntity.ch, currentEntity.position, currentEntity.ch, length, currentEntity.count-currentEntity.position);
260 currentEntity.count += length-currentEntity.position;
261 } else {
262 // have to reintroduce some whitespace so this parses:
263 for(int i=length; i<currentEntity.position; i++)
264 currentEntity.ch[i]=' ';
265 }
266 // prepend contents...
267 System.arraycopy(scannedChars, 0, currentEntity.ch, 0, length);
268 currentEntity.position = 0;
269 currentEntity.baseCharOffset = 0;
270 currentEntity.startPosition = 0;
271 currentEntity.columnNumber = currentEntity.lineNumber = 1;
272 }
273
274 } // class XMLVersionDetector