View Javadoc
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