001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: package org.apache.xerces.impl;
019:
020: import java.io.CharConversionException;
021: import java.io.EOFException;
022: import java.io.IOException;
023:
024: import org.apache.xerces.impl.io.MalformedByteSequenceException;
025: import org.apache.xerces.impl.msg.XMLMessageFormatter;
026: import org.apache.xerces.util.SymbolTable;
027: import org.apache.xerces.xni.parser.XMLComponentManager;
028: import org.apache.xerces.xni.parser.XMLConfigurationException;
029: import org.apache.xerces.xni.parser.XMLInputSource;
030:
031: /**
032: * This class scans the version of the document to determine
033: * which scanner to use: XML 1.1 or XML 1.0.
034: * The version is scanned using XML 1.1. scanner.
035: *
036: * @xerces.internal
037: *
038: * @author Neil Graham, IBM
039: * @author Elena Litani, IBM
040: * @version $Id: XMLVersionDetector.java 572055 2007-09-02 17:55:43Z mrglavas $
041: */
042: public class XMLVersionDetector {
043:
044: //
045: // Constants
046: //
047:
048: private static final char[] XML11_VERSION = new char[] { '1', '.',
049: '1' };
050:
051: // property identifiers
052:
053: /** Property identifier: symbol table. */
054: protected static final String SYMBOL_TABLE = Constants.XERCES_PROPERTY_PREFIX
055: + Constants.SYMBOL_TABLE_PROPERTY;
056:
057: /** Property identifier: error reporter. */
058: protected static final String ERROR_REPORTER = Constants.XERCES_PROPERTY_PREFIX
059: + Constants.ERROR_REPORTER_PROPERTY;
060:
061: /** Property identifier: entity manager. */
062: protected static final String ENTITY_MANAGER = Constants.XERCES_PROPERTY_PREFIX
063: + Constants.ENTITY_MANAGER_PROPERTY;
064:
065: //
066: // Data
067: //
068:
069: /** Symbol: "version". */
070: protected static final String fVersionSymbol = "version".intern();
071:
072: // symbol: [xml]:
073: protected static final String fXMLSymbol = "[xml]".intern();
074:
075: /** Symbol table. */
076: protected SymbolTable fSymbolTable;
077:
078: /** Error reporter. */
079: protected XMLErrorReporter fErrorReporter;
080:
081: /** Entity manager. */
082: protected XMLEntityManager fEntityManager;
083:
084: protected String fEncoding = null;
085:
086: private final char[] fExpectedVersionString = { '<', '?', 'x', 'm',
087: 'l', ' ', 'v', 'e', 'r', 's', 'i', 'o', 'n', '=', ' ', ' ',
088: ' ', ' ', ' ' };
089:
090: /**
091: *
092: *
093: * @param componentManager The component manager.
094: *
095: * @throws XNIException Throws exception if required features and
096: * properties cannot be found.
097: */
098: public void reset(XMLComponentManager componentManager)
099: throws XMLConfigurationException {
100:
101: // Xerces properties
102: fSymbolTable = (SymbolTable) componentManager
103: .getProperty(SYMBOL_TABLE);
104: fErrorReporter = (XMLErrorReporter) componentManager
105: .getProperty(ERROR_REPORTER);
106: fEntityManager = (XMLEntityManager) componentManager
107: .getProperty(ENTITY_MANAGER);
108: for (int i = 14; i < fExpectedVersionString.length; i++)
109: fExpectedVersionString[i] = ' ';
110: } // reset(XMLComponentManager)
111:
112: /**
113: * Reset the reference to the appropriate scanner given the version of the
114: * document and start document scanning.
115: * @param scanner - the scanner to use
116: * @param version - the version of the document (XML 1.1 or XML 1.0).
117: */
118: public void startDocumentParsing(XMLEntityHandler scanner,
119: short version) {
120:
121: if (version == Constants.XML_VERSION_1_0) {
122: fEntityManager.setScannerVersion(Constants.XML_VERSION_1_0);
123: } else {
124: fEntityManager.setScannerVersion(Constants.XML_VERSION_1_1);
125: }
126: // Make sure the locator used by the error reporter is the current entity scanner.
127: fErrorReporter.setDocumentLocator(fEntityManager
128: .getEntityScanner());
129:
130: // Note: above we reset fEntityScanner in the entity manager, thus in startEntity
131: // in each scanner fEntityScanner field must be reset to reflect the change.
132: //
133: fEntityManager.setEntityHandler(scanner);
134:
135: scanner.startEntity(fXMLSymbol, fEntityManager
136: .getCurrentResourceIdentifier(), fEncoding, null);
137: }
138:
139: /**
140: * This methods scans the XML declaration to find out the version
141: * (and provisional encoding) of the document.
142: * The scanning is doing using XML 1.1 scanner.
143: * @param inputSource
144: * @return short - Constants.XML_VERSION_1_1 if document version 1.1,
145: * otherwise Constants.XML_VERSION_1_0
146: * @throws IOException
147: */
148: public short determineDocVersion(XMLInputSource inputSource)
149: throws IOException {
150: fEncoding = fEntityManager.setupCurrentEntity(fXMLSymbol,
151: inputSource, false, true);
152:
153: // Must use XML 1.0 scanner to handle whitespace correctly
154: // in the XML declaration.
155: fEntityManager.setScannerVersion(Constants.XML_VERSION_1_0);
156: XMLEntityScanner scanner = fEntityManager.getEntityScanner();
157: try {
158: if (!scanner.skipString("<?xml")) {
159: // definitely not a well-formed 1.1 doc!
160: return Constants.XML_VERSION_1_0;
161: }
162: if (!scanner.skipDeclSpaces()) {
163: fixupCurrentEntity(fEntityManager,
164: fExpectedVersionString, 5);
165: return Constants.XML_VERSION_1_0;
166: }
167: if (!scanner.skipString("version")) {
168: fixupCurrentEntity(fEntityManager,
169: fExpectedVersionString, 6);
170: return Constants.XML_VERSION_1_0;
171: }
172: scanner.skipDeclSpaces();
173: // Check if the next character is '='. If it is then consume it.
174: if (scanner.peekChar() != '=') {
175: fixupCurrentEntity(fEntityManager,
176: fExpectedVersionString, 13);
177: return Constants.XML_VERSION_1_0;
178: }
179: scanner.scanChar();
180: scanner.skipDeclSpaces();
181: int quoteChar = scanner.scanChar();
182: fExpectedVersionString[14] = (char) quoteChar;
183: for (int versionPos = 0; versionPos < XML11_VERSION.length; versionPos++) {
184: fExpectedVersionString[15 + versionPos] = (char) scanner
185: .scanChar();
186: }
187: // REVISIT: should we check whether this equals quoteChar?
188: fExpectedVersionString[18] = (char) scanner.scanChar();
189: fixupCurrentEntity(fEntityManager, fExpectedVersionString,
190: 19);
191: int matched = 0;
192: for (; matched < XML11_VERSION.length; matched++) {
193: if (fExpectedVersionString[15 + matched] != XML11_VERSION[matched])
194: break;
195: }
196: return (matched == XML11_VERSION.length) ? Constants.XML_VERSION_1_1
197: : Constants.XML_VERSION_1_0;
198: }
199: // encoding errors
200: catch (MalformedByteSequenceException e) {
201: fErrorReporter.reportError(e.getDomain(), e.getKey(), e
202: .getArguments(),
203: XMLErrorReporter.SEVERITY_FATAL_ERROR, e);
204: return Constants.XML_VERSION_ERROR;
205: } catch (CharConversionException e) {
206: fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
207: "CharConversionFailure", null,
208: XMLErrorReporter.SEVERITY_FATAL_ERROR, e);
209: return Constants.XML_VERSION_ERROR;
210: }
211: // premature end of file
212: catch (EOFException e) {
213: fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
214: "PrematureEOF", null,
215: XMLErrorReporter.SEVERITY_FATAL_ERROR);
216: return Constants.XML_VERSION_ERROR;
217: }
218: }
219:
220: // This method prepends "length" chars from the char array,
221: // from offset 0, to the manager's fCurrentEntity.ch.
222: private void fixupCurrentEntity(XMLEntityManager manager,
223: char[] scannedChars, int length) {
224: XMLEntityManager.ScannedEntity currentEntity = manager
225: .getCurrentEntity();
226: if (currentEntity.count - currentEntity.position + length > currentEntity.ch.length) {
227: //resize array; this case is hard to imagine...
228: char[] tempCh = currentEntity.ch;
229: currentEntity.ch = new char[length + currentEntity.count
230: - currentEntity.position + 1];
231: System.arraycopy(tempCh, 0, currentEntity.ch, 0,
232: tempCh.length);
233: }
234: if (currentEntity.position < length) {
235: // have to move sensitive stuff out of the way...
236: System.arraycopy(currentEntity.ch, currentEntity.position,
237: currentEntity.ch, length, currentEntity.count
238: - currentEntity.position);
239: currentEntity.count += length - currentEntity.position;
240: } else {
241: // have to reintroduce some whitespace so this parses:
242: for (int i = length; i < currentEntity.position; i++)
243: currentEntity.ch[i] = ' ';
244: }
245: // prepend contents...
246: System.arraycopy(scannedChars, 0, currentEntity.ch, 0, length);
247: currentEntity.position = 0;
248: currentEntity.baseCharOffset = 0;
249: currentEntity.startPosition = 0;
250: currentEntity.columnNumber = currentEntity.lineNumber = 1;
251: }
252:
253: } // class XMLVersionDetector
|