1 /* 2 * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. 3 */ 4 /* 5 * Licensed to the Apache Software Foundation (ASF) under one or more 6 * contributor license agreements. See the NOTICE file distributed with 7 * this work for additional information regarding copyright ownership. 8 * The ASF licenses this file to You under the Apache License, Version 2.0 9 * (the "License"); you may not use this file except in compliance with 10 * the License. You may obtain a copy of the License at 11 * 12 * http://www.apache.org/licenses/LICENSE-2.0 13 * 14 * Unless required by applicable law or agreed to in writing, software 15 * distributed under the License is distributed on an "AS IS" BASIS, 16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 * See the License for the specific language governing permissions and 18 * limitations under the License. 19 */ 20 21 package com.sun.org.apache.xerces.internal.impl; 22 23 import java.io.EOFException; 24 import java.io.IOException; 25 26 import com.sun.org.apache.xerces.internal.impl.msg.XMLMessageFormatter; 27 import com.sun.org.apache.xerces.internal.util.SymbolTable; 28 import com.sun.org.apache.xerces.internal.xni.XMLString; 29 import com.sun.org.apache.xerces.internal.xni.parser.XMLComponentManager; 30 import com.sun.org.apache.xerces.internal.xni.parser.XMLConfigurationException; 31 import com.sun.org.apache.xerces.internal.xni.parser.XMLInputSource; 32 import com.sun.xml.internal.stream.Entity.ScannedEntity; 33 34 /** 35 * This class scans the version of the document to determine 36 * which scanner to use: XML 1.1 or XML 1.0. 37 * The version is scanned using XML 1.1. scanner. 38 * 39 * @xerces.internal 40 * 41 * @author Neil Graham, IBM 42 * @author Elena Litani, IBM 43 */ 44 public class XMLVersionDetector { 45 46 // 47 // Constants 48 // 49 50 private final static char[] XML11_VERSION = new char[]{'1', '.', '1'}; 51 52 53 // property identifiers 54 55 /** Property identifier: symbol table. */ 56 protected static final String SYMBOL_TABLE = 57 Constants.XERCES_PROPERTY_PREFIX + Constants.SYMBOL_TABLE_PROPERTY; 58 59 /** Property identifier: error reporter. */ 60 protected static final String ERROR_REPORTER = 61 Constants.XERCES_PROPERTY_PREFIX + Constants.ERROR_REPORTER_PROPERTY; 62 63 /** Property identifier: entity manager. */ 64 protected static final String ENTITY_MANAGER = 65 Constants.XERCES_PROPERTY_PREFIX + Constants.ENTITY_MANAGER_PROPERTY; 66 67 // 68 // Data 69 // 70 71 /** Symbol: "version". */ 72 protected final static String fVersionSymbol = "version".intern(); 73 74 // symbol: [xml]: 75 protected static final String fXMLSymbol = "[xml]".intern(); 76 77 /** Symbol table. */ 78 protected SymbolTable fSymbolTable; 79 80 /** Error reporter. */ 81 protected XMLErrorReporter fErrorReporter; 82 83 /** Entity manager. */ 84 protected XMLEntityManager fEntityManager; 85 86 protected String fEncoding = null; 87 88 private XMLString fVersionNum = new XMLString(); 89 90 private final char [] fExpectedVersionString = {'<', '?', 'x', 'm', 'l', ' ', 'v', 'e', 'r', 's', 91 'i', 'o', 'n', '=', ' ', ' ', ' ', ' ', ' '}; 92 93 /** 94 * 95 * 96 * @param componentManager The component manager. 97 * 98 * @throws SAXException Throws exception if required features and 99 * properties cannot be found. 100 */ reset(XMLComponentManager componentManager)101 public void reset(XMLComponentManager componentManager) 102 throws XMLConfigurationException { 103 104 // Xerces properties 105 fSymbolTable = (SymbolTable)componentManager.getProperty(SYMBOL_TABLE); 106 fErrorReporter = (XMLErrorReporter)componentManager.getProperty(ERROR_REPORTER); 107 fEntityManager = (XMLEntityManager)componentManager.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 */ startDocumentParsing(XMLEntityHandler scanner, short version)118 public void startDocumentParsing(XMLEntityHandler scanner, short version){ 119 120 if (version == Constants.XML_VERSION_1_0){ 121 fEntityManager.setScannerVersion(Constants.XML_VERSION_1_0); 122 } 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.getEntityScanner()); 128 129 // Note: above we reset fEntityScanner in the entity manager, thus in startEntity 130 // in each scanner fEntityScanner field must be reset to reflect the change. 131 // 132 fEntityManager.setEntityHandler(scanner); 133 134 scanner.startEntity(fXMLSymbol, fEntityManager.getCurrentResourceIdentifier(), fEncoding, null); 135 } 136 137 138 /** 139 * This methods scans the XML declaration to find out the version 140 * (and provisional encoding) of the document. 141 * The scanning is doing using XML 1.1 scanner. 142 * @param inputSource 143 * @return short - Constants.XML_VERSION_1_1 if document version 1.1, 144 * otherwise Constants.XML_VERSION_1_0 145 * @throws IOException 146 */ determineDocVersion(XMLInputSource inputSource)147 public short determineDocVersion(XMLInputSource inputSource) throws IOException { 148 fEncoding = fEntityManager.setupCurrentEntity(false, fXMLSymbol, inputSource, false, true); 149 150 // Must use XML 1.0 scanner to handle whitespace correctly 151 // in the XML declaration. 152 fEntityManager.setScannerVersion(Constants.XML_VERSION_1_0); 153 XMLEntityScanner scanner = fEntityManager.getEntityScanner(); 154 scanner.detectingVersion = true; 155 try { 156 if (!scanner.skipString("<?xml")) { 157 // definitely not a well-formed 1.1 doc! 158 scanner.detectingVersion = false; 159 return Constants.XML_VERSION_1_0; 160 } 161 if (!scanner.skipDeclSpaces()) { 162 fixupCurrentEntity(fEntityManager, fExpectedVersionString, 5); 163 scanner.detectingVersion = false; 164 return Constants.XML_VERSION_1_0; 165 } 166 if (!scanner.skipString("version")) { 167 fixupCurrentEntity(fEntityManager, fExpectedVersionString, 6); 168 scanner.detectingVersion = false; 169 return Constants.XML_VERSION_1_0; 170 } 171 scanner.skipDeclSpaces(); 172 // Check if the next character is '='. If it is then consume it. 173 if (scanner.peekChar() != '=') { 174 fixupCurrentEntity(fEntityManager, fExpectedVersionString, 13); 175 scanner.detectingVersion = false; 176 return Constants.XML_VERSION_1_0; 177 } 178 scanner.scanChar(null); 179 scanner.skipDeclSpaces(); 180 int quoteChar = scanner.scanChar(null); 181 fExpectedVersionString[14] = (char) quoteChar; 182 for (int versionPos = 0; versionPos < XML11_VERSION.length; versionPos++) { 183 fExpectedVersionString[15 + versionPos] = (char) scanner.scanChar(null); 184 } 185 // REVISIT: should we check whether this equals quoteChar? 186 fExpectedVersionString[18] = (char) scanner.scanChar(null); 187 fixupCurrentEntity(fEntityManager, fExpectedVersionString, 19); 188 int matched = 0; 189 for (; matched < XML11_VERSION.length; matched++) { 190 if (fExpectedVersionString[15 + matched] != XML11_VERSION[matched]) 191 break; 192 } 193 scanner.detectingVersion = false; 194 if (matched == XML11_VERSION.length) 195 return Constants.XML_VERSION_1_1; 196 return Constants.XML_VERSION_1_0; 197 // premature end of file 198 } 199 catch (EOFException e) { 200 fErrorReporter.reportError( 201 XMLMessageFormatter.XML_DOMAIN, 202 "PrematureEOF", 203 null, 204 XMLErrorReporter.SEVERITY_FATAL_ERROR); 205 scanner.detectingVersion = false; 206 return Constants.XML_VERSION_1_0; 207 } 208 } 209 210 // This method prepends "length" chars from the char array, 211 // from offset 0, to the manager's fCurrentEntity.ch. fixupCurrentEntity(XMLEntityManager manager, char [] scannedChars, int length)212 private void fixupCurrentEntity(XMLEntityManager manager, 213 char [] scannedChars, int length) { 214 ScannedEntity currentEntity = manager.getCurrentEntity(); 215 if(currentEntity.count-currentEntity.position+length > currentEntity.ch.length) { 216 //resize array; this case is hard to imagine... 217 char[] tempCh = currentEntity.ch; 218 currentEntity.ch = new char[length+currentEntity.count-currentEntity.position+1]; 219 System.arraycopy(tempCh, 0, currentEntity.ch, 0, tempCh.length); 220 } 221 if(currentEntity.position < length) { 222 // have to move sensitive stuff out of the way... 223 System.arraycopy(currentEntity.ch, currentEntity.position, currentEntity.ch, length, currentEntity.count-currentEntity.position); 224 currentEntity.count += length-currentEntity.position; 225 } else { 226 // have to reintroduce some whitespace so this parses: 227 for(int i=length; i<currentEntity.position; i++) 228 currentEntity.ch[i]=' '; 229 } 230 // prepend contents... 231 System.arraycopy(scannedChars, 0, currentEntity.ch, 0, length); 232 currentEntity.position = 0; 233 currentEntity.baseCharOffset = 0; 234 currentEntity.startPosition = 0; 235 currentEntity.columnNumber = currentEntity.lineNumber = 1; 236 } 237 238 } // class XMLVersionDetector 239