|
|
|
|
|
*/ |
|
|
|
/* |
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
|
* contributor license agreements. See the NOTICE file distributed with |
|
* this work for additional information regarding copyright ownership. |
|
* The ASF licenses this file to You under the Apache License, Version 2.0 |
|
* (the "License"); you may not use this file except in compliance with |
|
* the License. You may obtain a copy of the License at |
|
* |
|
* http://www.apache.org/licenses/LICENSE-2.0 |
|
* |
|
* Unless required by applicable law or agreed to in writing, software |
|
* distributed under the License is distributed on an "AS IS" BASIS, |
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
* See the License for the specific language governing permissions and |
|
* limitations under the License. |
|
*/ |
|
|
|
package com.sun.org.apache.xerces.internal.impl; |
|
|
|
import com.sun.org.apache.xerces.internal.impl.io.MalformedByteSequenceException; |
|
import com.sun.org.apache.xerces.internal.impl.msg.XMLMessageFormatter; |
|
import com.sun.org.apache.xerces.internal.util.AugmentationsImpl; |
|
import com.sun.org.apache.xerces.internal.util.XMLAttributesIteratorImpl; |
|
import com.sun.org.apache.xerces.internal.util.XMLChar; |
|
import com.sun.org.apache.xerces.internal.util.XMLStringBuffer; |
|
import com.sun.org.apache.xerces.internal.util.XMLSymbols; |
|
import com.sun.org.apache.xerces.internal.utils.XMLSecurityManager.Limit; |
|
import com.sun.org.apache.xerces.internal.utils.XMLSecurityManager; |
|
import com.sun.org.apache.xerces.internal.utils.XMLSecurityPropertyManager; |
|
import com.sun.org.apache.xerces.internal.xni.Augmentations; |
|
import com.sun.org.apache.xerces.internal.xni.QName; |
|
import com.sun.org.apache.xerces.internal.xni.XMLAttributes; |
|
import com.sun.org.apache.xerces.internal.xni.XMLDocumentHandler; |
|
import com.sun.org.apache.xerces.internal.xni.XMLResourceIdentifier; |
|
import com.sun.org.apache.xerces.internal.xni.XMLString; |
|
import com.sun.org.apache.xerces.internal.xni.XNIException; |
|
import com.sun.org.apache.xerces.internal.xni.parser.XMLComponent; |
|
import com.sun.org.apache.xerces.internal.xni.parser.XMLComponentManager; |
|
import com.sun.org.apache.xerces.internal.xni.parser.XMLConfigurationException; |
|
import com.sun.org.apache.xerces.internal.xni.parser.XMLDocumentScanner; |
|
import com.sun.org.apache.xerces.internal.xni.parser.XMLInputSource; |
|
import com.sun.xml.internal.stream.XMLBufferListener; |
|
import com.sun.xml.internal.stream.XMLEntityStorage; |
|
import com.sun.xml.internal.stream.dtd.DTDGrammarUtil; |
|
import java.io.CharConversionException; |
|
import java.io.EOFException; |
|
import java.io.IOException; |
|
import javax.xml.XMLConstants; |
|
import javax.xml.stream.XMLInputFactory; |
|
import javax.xml.stream.XMLStreamConstants; |
|
import javax.xml.stream.events.XMLEvent; |
|
import jdk.xml.internal.JdkConstants; |
|
import jdk.xml.internal.JdkXmlUtils; |
|
import jdk.xml.internal.SecuritySupport; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public class XMLDocumentFragmentScannerImpl |
|
extends XMLScanner |
|
implements XMLDocumentScanner, XMLComponent, XMLEntityHandler, XMLBufferListener { |
|
|
|
// |
|
// Constants |
|
// |
|
|
|
protected int fElementAttributeLimit, fXMLNameLimit; |
|
|
|
|
|
protected ExternalSubsetResolver fExternalSubsetResolver; |
|
|
|
// scanner states |
|
|
|
//XXX this should be divided into more states. |
|
|
|
protected static final int SCANNER_STATE_START_OF_MARKUP = 21; |
|
|
|
|
|
protected static final int SCANNER_STATE_CONTENT = 22; |
|
|
|
|
|
protected static final int SCANNER_STATE_PI = 23; |
|
|
|
|
|
protected static final int SCANNER_STATE_DOCTYPE = 24; |
|
|
|
|
|
protected static final int SCANNER_STATE_XML_DECL = 25; |
|
|
|
|
|
protected static final int SCANNER_STATE_ROOT_ELEMENT = 26; |
|
|
|
|
|
protected static final int SCANNER_STATE_COMMENT = 27; |
|
|
|
|
|
protected static final int SCANNER_STATE_REFERENCE = 28; |
|
|
|
|
|
protected static final int SCANNER_STATE_ATTRIBUTE = 29; |
|
|
|
|
|
protected static final int SCANNER_STATE_ATTRIBUTE_VALUE = 30; |
|
|
|
/** Scanner state: trailing misc. USED BY DOCUMENT_SCANNER_IMPL*/ |
|
//protected static final int SCANNER_STATE_TRAILING_MISC = 32; |
|
|
|
|
|
protected static final int SCANNER_STATE_END_OF_INPUT = 33; |
|
|
|
|
|
protected static final int SCANNER_STATE_TERMINATED = 34; |
|
|
|
|
|
protected static final int SCANNER_STATE_CDATA = 35; |
|
|
|
|
|
protected static final int SCANNER_STATE_TEXT_DECL = 36; |
|
|
|
|
|
protected static final int SCANNER_STATE_CHARACTER_DATA = 37; |
|
|
|
|
|
protected static final int SCANNER_STATE_START_ELEMENT_TAG = 38; |
|
|
|
|
|
protected static final int SCANNER_STATE_END_ELEMENT_TAG = 39; |
|
|
|
protected static final int SCANNER_STATE_CHAR_REFERENCE = 40; |
|
protected static final int SCANNER_STATE_BUILT_IN_REFS = 41; |
|
|
|
// feature identifiers |
|
|
|
|
|
|
|
protected static final String NOTIFY_BUILTIN_REFS = |
|
Constants.XERCES_FEATURE_PREFIX + Constants.NOTIFY_BUILTIN_REFS_FEATURE; |
|
|
|
|
|
protected static final String ENTITY_RESOLVER = |
|
Constants.XERCES_PROPERTY_PREFIX + Constants.ENTITY_RESOLVER_PROPERTY; |
|
|
|
|
|
protected static final String STANDARD_URI_CONFORMANT = |
|
Constants.XERCES_FEATURE_PREFIX +Constants.STANDARD_URI_CONFORMANT_FEATURE; |
|
|
|
|
|
protected static final String CREATE_ENTITY_REF_NODES = |
|
Constants.XERCES_FEATURE_PREFIX + Constants.CREATE_ENTITY_REF_NODES_FEATURE; |
|
|
|
|
|
private static final String XML_SECURITY_PROPERTY_MANAGER = |
|
JdkConstants.XML_SECURITY_PROPERTY_MANAGER; |
|
|
|
|
|
|
|
*/ |
|
final static String EXTERNAL_ACCESS_DEFAULT = JdkConstants.EXTERNAL_ACCESS_DEFAULT; |
|
|
|
// recognized features and properties |
|
|
|
|
|
private static final String[] RECOGNIZED_FEATURES = { |
|
NAMESPACES, |
|
VALIDATION, |
|
NOTIFY_BUILTIN_REFS, |
|
NOTIFY_CHAR_REFS, |
|
Constants.STAX_REPORT_CDATA_EVENT, |
|
XMLConstants.USE_CATALOG |
|
}; |
|
|
|
|
|
private static final Boolean[] FEATURE_DEFAULTS = { |
|
Boolean.TRUE, |
|
null, |
|
Boolean.FALSE, |
|
Boolean.FALSE, |
|
Boolean.TRUE, |
|
JdkXmlUtils.USE_CATALOG_DEFAULT |
|
}; |
|
|
|
|
|
private static final String[] RECOGNIZED_PROPERTIES = { |
|
SYMBOL_TABLE, |
|
ERROR_REPORTER, |
|
ENTITY_MANAGER, |
|
XML_SECURITY_PROPERTY_MANAGER, |
|
JdkXmlUtils.CATALOG_DEFER, |
|
JdkXmlUtils.CATALOG_FILES, |
|
JdkXmlUtils.CATALOG_PREFER, |
|
JdkXmlUtils.CATALOG_RESOLVE, |
|
JdkConstants.CDATA_CHUNK_SIZE |
|
}; |
|
|
|
|
|
private static final Object[] PROPERTY_DEFAULTS = { |
|
null, |
|
null, |
|
null, |
|
null, |
|
null, |
|
null, |
|
null, |
|
null, |
|
JdkConstants.CDATA_CHUNK_SIZE_DEFAULT |
|
}; |
|
|
|
|
|
private static final char [] CDATA = {'[','C','D','A','T','A','['}; |
|
static final char [] XMLDECL = {'<','?','x','m','l'}; |
|
// private static final char [] endTag = {'<','/'}; |
|
// debugging |
|
|
|
|
|
private static final boolean DEBUG_SCANNER_STATE = false; |
|
|
|
|
|
private static final boolean DEBUG_DISPATCHER = false; |
|
|
|
|
|
protected static final boolean DEBUG_START_END_ELEMENT = false; |
|
|
|
|
|
protected static final boolean DEBUG = false; |
|
|
|
// |
|
// Data |
|
// |
|
|
|
// protected data |
|
|
|
|
|
protected XMLDocumentHandler fDocumentHandler; |
|
protected int fScannerLastState ; |
|
|
|
|
|
protected XMLEntityStorage fEntityStore; |
|
|
|
|
|
protected int[] fEntityStack = new int[4]; |
|
|
|
|
|
protected int fMarkupDepth; |
|
|
|
|
|
protected boolean fEmptyElement ; |
|
|
|
//track if we are reading attributes, this is usefule while |
|
|
|
protected boolean fReadingAttributes = false; |
|
|
|
|
|
protected int fScannerState; |
|
|
|
|
|
protected boolean fInScanContent = false; |
|
protected boolean fLastSectionWasCData = false; |
|
protected boolean fCDataStart = false; |
|
protected boolean fInCData = false; |
|
protected boolean fCDataEnd = false; |
|
protected boolean fLastSectionWasEntityReference = false; |
|
protected boolean fLastSectionWasCharacterData = false; |
|
|
|
|
|
protected boolean fHasExternalDTD; |
|
|
|
|
|
protected boolean fStandaloneSet; |
|
protected boolean fStandalone; |
|
protected String fVersion; |
|
|
|
// element information |
|
|
|
|
|
protected QName fCurrentElement; |
|
|
|
|
|
protected ElementStack fElementStack = new ElementStack(); |
|
protected ElementStack2 fElementStack2 = new ElementStack2(); |
|
|
|
// other info |
|
|
|
/** Document system identifier. |
|
* REVISIT: So what's this used for? - NG |
|
* protected String fDocumentSystemId; |
|
******/ |
|
|
|
protected String fPITarget ; |
|
|
|
|
|
protected XMLString fPIData = new XMLString(); |
|
|
|
// features |
|
|
|
|
|
|
|
protected boolean fNotifyBuiltInRefs = false; |
|
|
|
//STAX related properties |
|
|
|
protected boolean fSupportDTD = true; |
|
protected boolean fReplaceEntityReferences = true; |
|
protected boolean fSupportExternalEntities = false; |
|
protected boolean fReportCdataEvent = false ; |
|
protected boolean fIsCoalesce = false ; |
|
protected String fDeclaredEncoding = null; |
|
|
|
protected boolean fDisallowDoctype = false; |
|
|
|
|
|
protected boolean fCreateEntityRefNodes = false; |
|
|
|
|
|
|
|
*/ |
|
private int fChunkSize; |
|
|
|
|
|
|
|
|
|
*/ |
|
protected String fAccessExternalDTD = EXTERNAL_ACCESS_DEFAULT; |
|
|
|
|
|
|
|
|
|
*/ |
|
protected boolean fStrictURI; |
|
|
|
// drivers |
|
|
|
|
|
protected Driver fDriver; |
|
|
|
|
|
protected Driver fContentDriver = createContentDriver(); |
|
|
|
// temporary variables |
|
|
|
|
|
protected QName fElementQName = new QName(); |
|
|
|
|
|
protected QName fAttributeQName = new QName(); |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
protected XMLAttributesIteratorImpl fAttributes = new XMLAttributesIteratorImpl(); |
|
|
|
|
|
|
|
protected XMLString fTempString = new XMLString(); |
|
|
|
|
|
protected XMLString fTempString2 = new XMLString(); |
|
|
|
|
|
private final String[] fStrings = new String[3]; |
|
|
|
|
|
protected XMLStringBuffer fStringBuffer = new XMLStringBuffer(); |
|
|
|
|
|
protected XMLStringBuffer fStringBuffer2 = new XMLStringBuffer(); |
|
|
|
/** stores character data. */ |
|
|
|
protected XMLStringBuffer fContentBuffer = new XMLStringBuffer(); |
|
|
|
|
|
private final char[] fSingleChar = new char[1]; |
|
private String fCurrentEntityName = null; |
|
|
|
|
|
protected boolean fScanToEnd = false; |
|
|
|
protected DTDGrammarUtil dtdGrammarUtil= null; |
|
|
|
protected boolean fAddDefaultAttr = false; |
|
|
|
protected boolean foundBuiltInRefs = false; |
|
|
|
|
|
protected boolean builtInRefCharacterHandled = false; |
|
|
|
|
|
static final short MAX_DEPTH_LIMIT = 5 ; |
|
static final short ELEMENT_ARRAY_LENGTH = 200 ; |
|
static final short MAX_POINTER_AT_A_DEPTH = 4 ; |
|
static final boolean DEBUG_SKIP_ALGORITHM = false; |
|
|
|
String [] fElementArray = new String[ELEMENT_ARRAY_LENGTH] ; |
|
|
|
short fLastPointerLocation = 0 ; |
|
short fElementPointer = 0 ; |
|
|
|
short [] [] fPointerInfo = new short[MAX_DEPTH_LIMIT] [MAX_POINTER_AT_A_DEPTH] ; |
|
protected String fElementRawname ; |
|
protected boolean fShouldSkip = false; |
|
protected boolean fAdd = false ; |
|
protected boolean fSkip = false; |
|
|
|
|
|
private Augmentations fTempAugmentations = null; |
|
// |
|
// Constructors |
|
// |
|
|
|
|
|
public XMLDocumentFragmentScannerImpl() { |
|
} // <init>() |
|
|
|
// |
|
// XMLDocumentScanner methods |
|
// |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public void setInputSource(XMLInputSource inputSource) throws IOException { |
|
fEntityManager.setEntityHandler(this); |
|
fEntityManager.startEntity(false, "$fragment$", inputSource, false, true); |
|
// fDocumentSystemId = fEntityManager.expandSystemId(inputSource.getSystemId()); |
|
} // setInputSource(XMLInputSource) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public boolean scanDocument(boolean complete) |
|
throws IOException, XNIException { |
|
|
|
|
|
fEntityManager.setEntityHandler(this); |
|
//System.out.println(" get Document Handler in NSDocumentHandler " + fDocumentHandler ); |
|
|
|
int event = next(); |
|
do { |
|
switch (event) { |
|
case XMLStreamConstants.START_DOCUMENT : |
|
|
|
break; |
|
case XMLStreamConstants.START_ELEMENT : |
|
//System.out.println(" in scann element"); |
|
|
|
break; |
|
case XMLStreamConstants.CHARACTERS : |
|
fEntityScanner.checkNodeCount(fEntityScanner.fCurrentEntity); |
|
fDocumentHandler.characters(getCharacterData(),null); |
|
break; |
|
case XMLStreamConstants.SPACE: |
|
//check if getCharacterData() is the right function to retrieve ignorableWhitespace information. |
|
//System.out.println("in the space"); |
|
|
|
break; |
|
case XMLStreamConstants.ENTITY_REFERENCE : |
|
fEntityScanner.checkNodeCount(fEntityScanner.fCurrentEntity); |
|
|
|
break; |
|
case XMLStreamConstants.PROCESSING_INSTRUCTION : |
|
fEntityScanner.checkNodeCount(fEntityScanner.fCurrentEntity); |
|
fDocumentHandler.processingInstruction(getPITarget(),getPIData(),null); |
|
break; |
|
case XMLStreamConstants.COMMENT : |
|
fEntityScanner.checkNodeCount(fEntityScanner.fCurrentEntity); |
|
fDocumentHandler.comment(getCharacterData(),null); |
|
break; |
|
case XMLStreamConstants.DTD : |
|
//all DTD related callbacks are handled in DTDScanner. |
|
//1. Stax doesn't define DTD states as it does for XML Document. |
|
|
|
break; |
|
case XMLStreamConstants.CDATA: |
|
fEntityScanner.checkNodeCount(fEntityScanner.fCurrentEntity); |
|
if (fCDataStart) { |
|
fDocumentHandler.startCDATA(null); |
|
fCDataStart = false; |
|
fInCData = true; |
|
} |
|
|
|
fDocumentHandler.characters(getCharacterData(),null); |
|
if (fCDataEnd) { |
|
fDocumentHandler.endCDATA(null); |
|
fCDataEnd = false; |
|
} |
|
break; |
|
case XMLStreamConstants.NOTATION_DECLARATION : |
|
break; |
|
case XMLStreamConstants.ENTITY_DECLARATION : |
|
break; |
|
case XMLStreamConstants.NAMESPACE : |
|
break; |
|
case XMLStreamConstants.ATTRIBUTE : |
|
break; |
|
case XMLStreamConstants.END_ELEMENT : |
|
//do not give callback here. |
|
//this callback is given in scanEndElement function. |
|
|
|
break; |
|
default : |
|
|
|
return false; |
|
|
|
} |
|
|
|
event = next(); |
|
//System.out.println("here in after calling next"); |
|
} while (event!=XMLStreamConstants.END_DOCUMENT && complete); |
|
|
|
if(event == XMLStreamConstants.END_DOCUMENT) { |
|
fDocumentHandler.endDocument(null); |
|
return false; |
|
} |
|
|
|
return true; |
|
|
|
} // scanDocument(boolean):boolean |
|
|
|
|
|
|
|
public com.sun.org.apache.xerces.internal.xni.QName getElementQName(){ |
|
if(fScannerLastState == XMLEvent.END_ELEMENT){ |
|
fElementQName.setValues(fElementStack.getLastPoppedElement()); |
|
} |
|
return fElementQName ; |
|
} |
|
|
|
/** return the next state on the input |
|
* @return int |
|
*/ |
|
|
|
public int next() throws IOException, XNIException { |
|
return fDriver.next(); |
|
} |
|
|
|
// |
|
// XMLComponent methods |
|
// |
|
|
|
/** |
|
* Resets the component. The component can query the component manager |
|
* about any features and properties that affect the operation of the |
|
* component. |
|
* |
|
* @param componentManager The component manager. |
|
* |
|
* @throws SAXException Thrown by component on initialization error. |
|
* For example, if a feature or property is |
|
* required for the operation of the component, the |
|
* component manager may throw a |
|
* SAXNotRecognizedException or a |
|
* SAXNotSupportedException. |
|
*/ |
|
|
|
public void reset(XMLComponentManager componentManager) |
|
throws XMLConfigurationException { |
|
|
|
super.reset(componentManager); |
|
|
|
// other settings |
|
// fDocumentSystemId = null; |
|
|
|
// sax features |
|
//fAttributes.setNamespaces(fNamespaces); |
|
|
|
|
|
fReportCdataEvent = componentManager.getFeature(Constants.STAX_REPORT_CDATA_EVENT, true); |
|
fSecurityManager = (XMLSecurityManager)componentManager.getProperty(Constants.SECURITY_MANAGER, null); |
|
fNotifyBuiltInRefs = componentManager.getFeature(NOTIFY_BUILTIN_REFS, false); |
|
|
|
fCreateEntityRefNodes = componentManager.getFeature(CREATE_ENTITY_REF_NODES, fCreateEntityRefNodes); |
|
|
|
Object resolver = componentManager.getProperty(ENTITY_RESOLVER, null); |
|
fExternalSubsetResolver = (resolver instanceof ExternalSubsetResolver) ? |
|
(ExternalSubsetResolver) resolver : null; |
|
|
|
|
|
fReadingAttributes = false; |
|
//xxx: external entities are supported in Xerces |
|
|
|
fSupportExternalEntities = true; |
|
fReplaceEntityReferences = true; |
|
fIsCoalesce = false; |
|
|
|
|
|
setScannerState(SCANNER_STATE_CONTENT); |
|
setDriver(fContentDriver); |
|
|
|
|
|
XMLSecurityPropertyManager spm = (XMLSecurityPropertyManager) |
|
componentManager.getProperty(XML_SECURITY_PROPERTY_MANAGER, null); |
|
fAccessExternalDTD = spm.getValue(XMLSecurityPropertyManager.Property.ACCESS_EXTERNAL_DTD); |
|
|
|
fStrictURI = componentManager.getFeature(STANDARD_URI_CONFORMANT, false); |
|
fChunkSize = JdkXmlUtils.getValue(componentManager.getProperty(JdkConstants.CDATA_CHUNK_SIZE), |
|
JdkConstants.CDATA_CHUNK_SIZE_DEFAULT); |
|
|
|
resetCommon(); |
|
//fEntityManager.test(); |
|
} // reset(XMLComponentManager) |
|
|
|
|
|
public void reset(PropertyManager propertyManager){ |
|
|
|
super.reset(propertyManager); |
|
|
|
// other settings |
|
|
|
fNamespaces = ((Boolean)propertyManager.getProperty(XMLInputFactory.IS_NAMESPACE_AWARE)); |
|
fNotifyBuiltInRefs = false ; |
|
|
|
//fElementStack2.clear(); |
|
//fReplaceEntityReferences = true; |
|
|
|
Boolean bo = (Boolean)propertyManager.getProperty(XMLInputFactory.IS_REPLACING_ENTITY_REFERENCES); |
|
fReplaceEntityReferences = bo; |
|
bo = (Boolean)propertyManager.getProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES); |
|
fSupportExternalEntities = bo; |
|
Boolean cdata = (Boolean)propertyManager.getProperty( |
|
Constants.ZEPHYR_PROPERTY_PREFIX + Constants.STAX_REPORT_CDATA_EVENT) ; |
|
if(cdata != null) |
|
fReportCdataEvent = cdata ; |
|
Boolean coalesce = (Boolean)propertyManager.getProperty(XMLInputFactory.IS_COALESCING) ; |
|
if(coalesce != null) |
|
fIsCoalesce = coalesce; |
|
fReportCdataEvent = fIsCoalesce ? false : (fReportCdataEvent && true) ; |
|
//if fIsCoalesce is set to true, set the value of fReplaceEntityReferences to true, |
|
|
|
fReplaceEntityReferences = fIsCoalesce ? true : fReplaceEntityReferences; |
|
// setup Driver |
|
//we dont need to do this -- nb. |
|
//setScannerState(SCANNER_STATE_CONTENT); |
|
//setDriver(fContentDriver); |
|
//fEntityManager.test(); |
|
|
|
|
|
XMLSecurityPropertyManager spm = (XMLSecurityPropertyManager) |
|
propertyManager.getProperty(XML_SECURITY_PROPERTY_MANAGER); |
|
fAccessExternalDTD = spm.getValue(XMLSecurityPropertyManager.Property.ACCESS_EXTERNAL_DTD); |
|
|
|
fSecurityManager = (XMLSecurityManager)propertyManager.getProperty(Constants.SECURITY_MANAGER); |
|
fChunkSize = JdkXmlUtils.getValue(propertyManager.getProperty(JdkConstants.CDATA_CHUNK_SIZE), |
|
JdkConstants.CDATA_CHUNK_SIZE_DEFAULT); |
|
resetCommon(); |
|
} // reset(XMLComponentManager) |
|
|
|
void resetCommon() { |
|
|
|
fMarkupDepth = 0; |
|
fCurrentElement = null; |
|
fElementStack.clear(); |
|
fHasExternalDTD = false; |
|
fStandaloneSet = false; |
|
fStandalone = false; |
|
fInScanContent = false; |
|
|
|
fShouldSkip = false; |
|
fAdd = false; |
|
fSkip = false; |
|
|
|
fEntityStore = fEntityManager.getEntityStore(); |
|
dtdGrammarUtil = null; |
|
|
|
if (fSecurityManager != null) { |
|
fElementAttributeLimit = fSecurityManager.getLimit(XMLSecurityManager.Limit.ELEMENT_ATTRIBUTE_LIMIT); |
|
fXMLNameLimit = fSecurityManager.getLimit(XMLSecurityManager.Limit.MAX_NAME_LIMIT); |
|
} else { |
|
fElementAttributeLimit = 0; |
|
fXMLNameLimit = XMLSecurityManager.Limit.MAX_NAME_LIMIT.defaultValue(); |
|
} |
|
fLimitAnalyzer = fEntityManager.fLimitAnalyzer; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public String[] getRecognizedFeatures() { |
|
return RECOGNIZED_FEATURES.clone(); |
|
} // getRecognizedFeatures():String[] |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public void setFeature(String featureId, boolean state) |
|
throws XMLConfigurationException { |
|
|
|
super.setFeature(featureId, state); |
|
|
|
|
|
if (featureId.startsWith(Constants.XERCES_FEATURE_PREFIX)) { |
|
String feature = featureId.substring(Constants.XERCES_FEATURE_PREFIX.length()); |
|
if (feature.equals(Constants.NOTIFY_BUILTIN_REFS_FEATURE)) { |
|
fNotifyBuiltInRefs = state; |
|
} |
|
} |
|
|
|
} // setFeature(String,boolean) |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public String[] getRecognizedProperties() { |
|
return RECOGNIZED_PROPERTIES.clone(); |
|
} // getRecognizedProperties():String[] |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public void setProperty(String propertyId, Object value) |
|
throws XMLConfigurationException { |
|
|
|
super.setProperty(propertyId, value); |
|
|
|
|
|
if (propertyId.startsWith(Constants.XERCES_PROPERTY_PREFIX)) { |
|
final int suffixLength = propertyId.length() - Constants.XERCES_PROPERTY_PREFIX.length(); |
|
if (suffixLength == Constants.ENTITY_MANAGER_PROPERTY.length() && |
|
propertyId.endsWith(Constants.ENTITY_MANAGER_PROPERTY)) { |
|
fEntityManager = (XMLEntityManager)value; |
|
return; |
|
} |
|
if (suffixLength == Constants.ENTITY_RESOLVER_PROPERTY.length() && |
|
propertyId.endsWith(Constants.ENTITY_RESOLVER_PROPERTY)) { |
|
fExternalSubsetResolver = (value instanceof ExternalSubsetResolver) ? |
|
(ExternalSubsetResolver) value : null; |
|
return; |
|
} |
|
} |
|
|
|
|
|
|
|
if (propertyId.startsWith(Constants.XERCES_PROPERTY_PREFIX)) { |
|
String property = propertyId.substring(Constants.XERCES_PROPERTY_PREFIX.length()); |
|
if (property.equals(Constants.ENTITY_MANAGER_PROPERTY)) { |
|
fEntityManager = (XMLEntityManager)value; |
|
} |
|
return; |
|
} |
|
|
|
|
|
if (propertyId.equals(XML_SECURITY_PROPERTY_MANAGER)) |
|
{ |
|
XMLSecurityPropertyManager spm = (XMLSecurityPropertyManager)value; |
|
fAccessExternalDTD = spm.getValue(XMLSecurityPropertyManager.Property.ACCESS_EXTERNAL_DTD); |
|
} |
|
|
|
} // setProperty(String,Object) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public Boolean getFeatureDefault(String featureId) { |
|
for (int i = 0; i < RECOGNIZED_FEATURES.length; i++) { |
|
if (RECOGNIZED_FEATURES[i].equals(featureId)) { |
|
return FEATURE_DEFAULTS[i]; |
|
} |
|
} |
|
return null; |
|
} // getFeatureDefault(String):Boolean |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public Object getPropertyDefault(String propertyId) { |
|
for (int i = 0; i < RECOGNIZED_PROPERTIES.length; i++) { |
|
if (RECOGNIZED_PROPERTIES[i].equals(propertyId)) { |
|
return PROPERTY_DEFAULTS[i]; |
|
} |
|
} |
|
return null; |
|
} // getPropertyDefault(String):Object |
|
|
|
// |
|
// XMLDocumentSource methods |
|
// |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public void setDocumentHandler(XMLDocumentHandler documentHandler) { |
|
fDocumentHandler = documentHandler; |
|
//System.out.println(" In Set DOCUMENT HANDLER" + fDocumentHandler + " scanner =" + this); |
|
} // setDocumentHandler(XMLDocumentHandler) |
|
|
|
|
|
|
|
public XMLDocumentHandler getDocumentHandler(){ |
|
return fDocumentHandler; |
|
} |
|
|
|
// |
|
// XMLEntityHandler methods |
|
// |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public void startEntity(String name, |
|
XMLResourceIdentifier identifier, |
|
String encoding, Augmentations augs) throws XNIException { |
|
|
|
|
|
if (fEntityDepth == fEntityStack.length) { |
|
int[] entityarray = new int[fEntityStack.length * 2]; |
|
System.arraycopy(fEntityStack, 0, entityarray, 0, fEntityStack.length); |
|
fEntityStack = entityarray; |
|
} |
|
fEntityStack[fEntityDepth] = fMarkupDepth; |
|
|
|
super.startEntity(name, identifier, encoding, augs); |
|
|
|
|
|
if(fStandalone && fEntityStore.isEntityDeclInExternalSubset(name)) { |
|
reportFatalError("MSG_REFERENCE_TO_EXTERNALLY_DECLARED_ENTITY_WHEN_STANDALONE", |
|
new Object[]{name}); |
|
} |
|
|
|
/** we are not calling the handlers yet.. */ |
|
|
|
if (fDocumentHandler != null && !fScanningAttribute) { |
|
if (!name.equals("[xml]")) { |
|
fDocumentHandler.startGeneralEntity(name, identifier, encoding, augs); |
|
} |
|
} |
|
|
|
} // startEntity(String,XMLResourceIdentifier,String) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public void endEntity(String name, Augmentations augs) throws IOException, XNIException { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
super.endEntity(name, augs); |
|
|
|
|
|
if (fMarkupDepth != fEntityStack[fEntityDepth]) { |
|
reportFatalError("MarkupEntityMismatch", null); |
|
} |
|
|
|
/**/ |
|
|
|
if (fDocumentHandler != null && !fScanningAttribute) { |
|
if (!name.equals("[xml]")) { |
|
fDocumentHandler.endGeneralEntity(name, augs); |
|
} |
|
} |
|
|
|
|
|
} // endEntity(String) |
|
|
|
// |
|
// Protected methods |
|
// |
|
|
|
// Driver factory methods |
|
|
|
|
|
protected Driver createContentDriver() { |
|
return new FragmentContentDriver(); |
|
} // createContentDriver():Driver |
|
|
|
// scanning methods |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
protected void scanXMLDeclOrTextDecl(boolean scanningTextDecl) |
|
throws IOException, XNIException { |
|
|
|
|
|
super.scanXMLDeclOrTextDecl(scanningTextDecl, fStrings); |
|
fMarkupDepth--; |
|
|
|
|
|
String version = fStrings[0]; |
|
String encoding = fStrings[1]; |
|
String standalone = fStrings[2]; |
|
fDeclaredEncoding = encoding; |
|
|
|
fStandaloneSet = standalone != null; |
|
fStandalone = fStandaloneSet && standalone.equals("yes"); |
|
///xxx see where its used.. this is not used anywhere. |
|
//it may be useful for entity to store this information |
|
|
|
fEntityManager.setStandalone(fStandalone); |
|
|
|
|
|
|
|
if (fDocumentHandler != null) { |
|
if (scanningTextDecl) { |
|
fDocumentHandler.textDecl(version, encoding, null); |
|
} else { |
|
fDocumentHandler.xmlDecl(version, encoding, standalone, null); |
|
} |
|
} |
|
|
|
if(version != null){ |
|
fEntityScanner.setVersion(version); |
|
fEntityScanner.setXMLVersion(version); |
|
} |
|
|
|
if (encoding != null && !fEntityScanner.getCurrentEntity().isEncodingExternallySpecified()) { |
|
fEntityScanner.setEncoding(encoding); |
|
} |
|
|
|
} // scanXMLDeclOrTextDecl(boolean) |
|
|
|
public String getPITarget(){ |
|
return fPITarget ; |
|
} |
|
|
|
public XMLStringBuffer getPIData(){ |
|
return fContentBuffer ; |
|
} |
|
|
|
|
|
public XMLString getCharacterData(){ |
|
if(fUsebuffer){ |
|
return fContentBuffer ; |
|
}else{ |
|
return fTempString; |
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
protected void scanPIData(String target, XMLStringBuffer data) |
|
throws IOException, XNIException { |
|
|
|
super.scanPIData(target, data); |
|
|
|
|
|
fPITarget = target ; |
|
|
|
fMarkupDepth--; |
|
|
|
} // scanPIData(String) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
protected void scanComment() throws IOException, XNIException { |
|
fContentBuffer.clear(); |
|
scanComment(fContentBuffer); |
|
|
|
fUsebuffer = true; |
|
fMarkupDepth--; |
|
|
|
} // scanComment() |
|
|
|
|
|
public String getComment(){ |
|
return fContentBuffer.toString(); |
|
} |
|
|
|
void addElement(String rawname){ |
|
if(fElementPointer < ELEMENT_ARRAY_LENGTH){ |
|
|
|
fElementArray[fElementPointer] = rawname ; |
|
//storing elemnetPointer for particular element depth |
|
|
|
if(DEBUG_SKIP_ALGORITHM){ |
|
StringBuffer sb = new StringBuffer() ; |
|
sb.append(" Storing element information ") ; |
|
sb.append(" fElementPointer = " + fElementPointer) ; |
|
sb.append(" fElementRawname = " + fElementQName.rawname) ; |
|
sb.append(" fElementStack.fDepth = " + fElementStack.fDepth); |
|
System.out.println(sb.toString()) ; |
|
} |
|
|
|
|
|
if(fElementStack.fDepth < MAX_DEPTH_LIMIT){ |
|
short column = storePointerForADepth(fElementPointer); |
|
if(column > 0){ |
|
short pointer = getElementPointer((short)fElementStack.fDepth, (short)(column - 1) ); |
|
//identity comparison shouldn't take much time and we can rely on this |
|
|
|
if(rawname == fElementArray[pointer]){ |
|
fShouldSkip = true ; |
|
fLastPointerLocation = pointer ; |
|
|
|
resetPointer((short)fElementStack.fDepth , column) ; |
|
fElementArray[fElementPointer] = null ; |
|
return ; |
|
}else{ |
|
fShouldSkip = false ; |
|
} |
|
} |
|
} |
|
fElementPointer++ ; |
|
} |
|
} |
|
|
|
|
|
void resetPointer(short depth, short column){ |
|
fPointerInfo[depth] [column] = (short)0; |
|
} |
|
|
|
|
|
short storePointerForADepth(short elementPointer){ |
|
short depth = (short) fElementStack.fDepth ; |
|
|
|
//Stores element pointer locations at particular depth , only 4 pointer locations |
|
|
|
for(short i = 0 ; i < MAX_POINTER_AT_A_DEPTH ; i++){ |
|
|
|
if(canStore(depth, i)){ |
|
fPointerInfo[depth][i] = elementPointer ; |
|
if(DEBUG_SKIP_ALGORITHM){ |
|
StringBuffer sb = new StringBuffer() ; |
|
sb.append(" Pointer information ") ; |
|
sb.append(" fElementPointer = " + fElementPointer) ; |
|
sb.append(" fElementStack.fDepth = " + fElementStack.fDepth); |
|
sb.append(" column = " + i ) ; |
|
System.out.println(sb.toString()) ; |
|
} |
|
return i; |
|
} |
|
//else |
|
//pointer was not stored because we reached the limit |
|
} |
|
return -1 ; |
|
} |
|
|
|
boolean canStore(short depth, short column){ |
|
//colum = 0 , means first element at particular depth |
|
//column = 1, means second element at particular depth |
|
|
|
return fPointerInfo[depth][column] == 0 ? true : false ; |
|
} |
|
|
|
|
|
short getElementPointer(short depth, short column){ |
|
//colum = 0 , means first element at particular depth |
|
//column = 1, means second element at particular depth |
|
|
|
return fPointerInfo[depth][column] ; |
|
} |
|
|
|
//this function assumes that string passed is not null and skips |
|
|
|
boolean skipFromTheBuffer(String rawname) throws IOException{ |
|
if(fEntityScanner.skipString(rawname)){ |
|
char c = (char)fEntityScanner.peekChar() ; |
|
//If the start element was completely skipped we should encounter either ' '(space), |
|
|
|
if( c == ' ' || c == '/' || c == '>'){ |
|
fElementRawname = rawname ; |
|
return true ; |
|
} else{ |
|
return false; |
|
} |
|
} else |
|
return false ; |
|
} |
|
|
|
boolean skipQElement(String rawname) throws IOException{ |
|
|
|
final int c = fEntityScanner.getChar(rawname.length()); |
|
|
|
if(XMLChar.isName(c)){ |
|
return false; |
|
}else{ |
|
return fEntityScanner.skipString(rawname); |
|
} |
|
} |
|
|
|
protected boolean skipElement() throws IOException { |
|
|
|
if(!fShouldSkip) return false ; |
|
|
|
if(fLastPointerLocation != 0){ |
|
|
|
String rawname = fElementArray[fLastPointerLocation + 1] ; |
|
if(rawname != null && skipFromTheBuffer(rawname)){ |
|
fLastPointerLocation++ ; |
|
if(DEBUG_SKIP_ALGORITHM){ |
|
System.out.println("Element " + fElementRawname + |
|
" was SKIPPED at pointer location = " + fLastPointerLocation); |
|
} |
|
return true ; |
|
} else{ |
|
|
|
fLastPointerLocation = 0 ; |
|
|
|
} |
|
} |
|
//xxx: we can put some logic here as from what column it should start looking |
|
//for now we always start at 0 |
|
//fallback to tolerant algorithm, it would look for differnt element stored at different |
|
|
|
return fShouldSkip && skipElement((short)0); |
|
|
|
} |
|
|
|
|
|
boolean skipElement(short column) throws IOException { |
|
short depth = (short)fElementStack.fDepth ; |
|
|
|
if(depth > MAX_DEPTH_LIMIT){ |
|
return fShouldSkip = false ; |
|
} |
|
for(short i = column ; i < MAX_POINTER_AT_A_DEPTH ; i++){ |
|
short pointer = getElementPointer(depth , i ) ; |
|
|
|
if(pointer == 0){ |
|
return fShouldSkip = false ; |
|
} |
|
|
|
if(fElementArray[pointer] != null && skipFromTheBuffer(fElementArray[pointer])){ |
|
if(DEBUG_SKIP_ALGORITHM){ |
|
System.out.println(); |
|
System.out.println("Element " + fElementRawname + " was SKIPPED at depth = " + |
|
fElementStack.fDepth + " column = " + column ); |
|
System.out.println(); |
|
} |
|
fLastPointerLocation = pointer ; |
|
return fShouldSkip = true ; |
|
} |
|
} |
|
return fShouldSkip = false ; |
|
} |
|
|
|
/** |
|
* Scans a start element. This method will handle the binding of |
|
* namespace information and notifying the handler of the start |
|
* of the element. |
|
* <p> |
|
* <pre> |
|
* [44] EmptyElemTag ::= '<' Name (S Attribute)* S? '/>' |
|
* [40] STag ::= '<' Name (S Attribute)* S? '>' |
|
* </pre> |
|
* <p> |
|
* <strong>Note:</strong> This method assumes that the leading |
|
* '<' character has been consumed. |
|
* <p> |
|
* <strong>Note:</strong> This method uses the fElementQName and |
|
* fAttributes variables. The contents of these variables will be |
|
* destroyed. The caller should copy important information out of |
|
* these variables before calling this method. |
|
* NB: Content in fAttributes is valid only till the state of the parser is XMLEvent.START_ELEMENT |
|
* |
|
* @return True if element is empty. (i.e. It matches |
|
* production [44]. |
|
*/ |
|
// fElementQName will have the details of element just read.. |
|
|
|
protected boolean scanStartElement() |
|
throws IOException, XNIException { |
|
|
|
if (DEBUG_START_END_ELEMENT) System.out.println( this.getClass().toString() + ">>> scanStartElement()"); |
|
|
|
if(fSkip && !fAdd){ |
|
//get the stored element -- if everything goes right this should match the |
|
//token in the buffer |
|
|
|
QName name = fElementStack.getNext(); |
|
|
|
if(DEBUG_SKIP_ALGORITHM){ |
|
System.out.println("Trying to skip String = " + name.rawname); |
|
} |
|
|
|
|
|
fSkip = fEntityScanner.skipString(name.rawname); |
|
|
|
if(fSkip){ |
|
if(DEBUG_SKIP_ALGORITHM){ |
|
System.out.println("Element SUCESSFULLY skipped = " + name.rawname); |
|
} |
|
fElementStack.push(); |
|
fElementQName = name; |
|
}else{ |
|
|
|
fElementStack.reposition(); |
|
if(DEBUG_SKIP_ALGORITHM){ |
|
System.out.println("Element was NOT skipped, REPOSITIONING stack" ); |
|
} |
|
} |
|
} |
|
|
|
//we are still at the stage of adding elements |
|
//the elements were not matched or |
|
|
|
if(!fSkip || fAdd){ |
|
|
|
fElementQName = fElementStack.nextElement(); |
|
|
|
if (fNamespaces) { |
|
fEntityScanner.scanQName(fElementQName, NameType.ELEMENTSTART); |
|
} else { |
|
String name = fEntityScanner.scanName(NameType.ELEMENTSTART); |
|
fElementQName.setValues(null, name, name, null); |
|
} |
|
|
|
if(DEBUG)System.out.println("Element scanned in start element is " + fElementQName.toString()); |
|
if(DEBUG_SKIP_ALGORITHM){ |
|
if(fAdd){ |
|
System.out.println("Elements are being ADDED -- elemet added is = " + |
|
fElementQName.rawname + " at count = " + fElementStack.fCount); |
|
} |
|
} |
|
|
|
} |
|
|
|
|
|
if(fAdd){ |
|
|
|
fElementStack.matchElement(fElementQName); |
|
} |
|
|
|
|
|
|
|
fCurrentElement = fElementQName; |
|
|
|
String rawname = fElementQName.rawname; |
|
|
|
fEmptyElement = false; |
|
|
|
fAttributes.removeAllAttributes(); |
|
|
|
checkDepth(rawname); |
|
if(!seekCloseOfStartTag()){ |
|
fReadingAttributes = true; |
|
fAttributeCacheUsedCount =0; |
|
fStringBufferIndex =0; |
|
fAddDefaultAttr = true; |
|
do { |
|
scanAttribute(fAttributes); |
|
if (fSecurityManager != null && !fSecurityManager.isNoLimit(fElementAttributeLimit) && |
|
fAttributes.getLength() > fElementAttributeLimit){ |
|
fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN, |
|
"ElementAttributeLimit", |
|
new Object[]{rawname, fElementAttributeLimit }, |
|
XMLErrorReporter.SEVERITY_FATAL_ERROR ); |
|
} |
|
|
|
} while (!seekCloseOfStartTag()); |
|
fReadingAttributes=false; |
|
} |
|
|
|
if (fEmptyElement) { |
|
|
|
fMarkupDepth--; |
|
|
|
|
|
if (fMarkupDepth < fEntityStack[fEntityDepth - 1]) { |
|
reportFatalError("ElementEntityMismatch", |
|
new Object[]{fCurrentElement.rawname}); |
|
} |
|
|
|
if (fDocumentHandler != null) { |
|
fDocumentHandler.emptyElement(fElementQName, fAttributes, null); |
|
} |
|
|
|
//We should not be popping out the context here in endELement becaause the namespace context is still |
|
//valid when parser is at the endElement state. |
|
//if (fNamespaces) { |
|
// fNamespaceContext.popContext(); |
|
//} |
|
|
|
|
|
fElementStack.popElement(); |
|
|
|
} else { |
|
|
|
if(dtdGrammarUtil != null) |
|
dtdGrammarUtil.startElement(fElementQName, fAttributes); |
|
if(fDocumentHandler != null){ |
|
//complete element and attributes are traversed in this function so we can send a callback |
|
//here. |
|
|
|
fDocumentHandler.startElement(fElementQName, fAttributes, null); |
|
} |
|
} |
|
|
|
|
|
if (DEBUG_START_END_ELEMENT) System.out.println(this.getClass().toString() + |
|
"<<< scanStartElement(): "+fEmptyElement); |
|
return fEmptyElement; |
|
|
|
} // scanStartElement():boolean |
|
|
|
|
|
|
|
|
|
*/ |
|
protected boolean seekCloseOfStartTag() throws IOException, XNIException { |
|
|
|
boolean sawSpace = fEntityScanner.skipSpaces(); |
|
|
|
|
|
final int c = fEntityScanner.peekChar(); |
|
if (c == '>') { |
|
fEntityScanner.scanChar(null); |
|
return true; |
|
} else if (c == '/') { |
|
fEntityScanner.scanChar(null); |
|
if (!fEntityScanner.skipChar('>', NameType.ELEMENTEND)) { |
|
reportFatalError("ElementUnterminated", |
|
new Object[]{fElementQName.rawname}); |
|
} |
|
fEmptyElement = true; |
|
return true; |
|
} else if (!isValidNameStartChar(c) || !sawSpace) { |
|
// Second chance. Check if this character is a high |
|
|
|
if (!isValidNameStartHighSurrogate(c) || !sawSpace) { |
|
reportFatalError("ElementUnterminated", |
|
new Object[]{fElementQName.rawname}); |
|
} |
|
} |
|
|
|
return false; |
|
} |
|
|
|
public boolean hasAttributes(){ |
|
return fAttributes.getLength() > 0; |
|
} |
|
|
|
|
|
public XMLAttributesIteratorImpl getAttributeIterator(){ |
|
if(dtdGrammarUtil != null && fAddDefaultAttr){ |
|
dtdGrammarUtil.addDTDDefaultAttrs(fElementQName,fAttributes); |
|
fAddDefaultAttr = false; |
|
} |
|
return fAttributes; |
|
} |
|
|
|
|
|
public boolean standaloneSet(){ |
|
return fStandaloneSet; |
|
} |
|
|
|
public boolean isStandAlone(){ |
|
return fStandalone ; |
|
} |
|
/** |
|
* Scans an attribute name value pair. |
|
* <p> |
|
* <pre> |
|
* [41] Attribute ::= Name Eq AttValue |
|
* </pre> |
|
* <p> |
|
* <strong>Note:</strong> This method assumes that the next |
|
* character on the stream is the first character of the attribute |
|
* name. |
|
* <p> |
|
* <strong>Note:</strong> This method uses the fAttributeQName and |
|
* fQName variables. The contents of these variables will be |
|
* destroyed. |
|
* |
|
* @param attributes The attributes list for the scanned attribute. |
|
*/ |
|
|
|
protected void scanAttribute(XMLAttributes attributes) |
|
throws IOException, XNIException { |
|
if (DEBUG_START_END_ELEMENT) System.out.println(this.getClass().toString() +">>> scanAttribute()"); |
|
|
|
|
|
if (fNamespaces) { |
|
fEntityScanner.scanQName(fAttributeQName, NameType.ATTRIBUTENAME); |
|
} else { |
|
String name = fEntityScanner.scanName(NameType.ATTRIBUTENAME); |
|
fAttributeQName.setValues(null, name, name, null); |
|
} |
|
|
|
|
|
fEntityScanner.skipSpaces(); |
|
if (!fEntityScanner.skipChar('=', NameType.ATTRIBUTE)) { |
|
reportFatalError("EqRequiredInAttribute", |
|
new Object[] {fCurrentElement.rawname, fAttributeQName.rawname}); |
|
} |
|
fEntityScanner.skipSpaces(); |
|
|
|
int attIndex = 0 ; |
|
|
|
boolean isVC = fHasExternalDTD && !fStandalone; |
|
//fTempString would store attribute value |
|
///fTempString2 would store attribute non-normalized value |
|
|
|
//this function doesn't use 'attIndex'. We are adding the attribute later |
|
//after we have figured out that current attribute is not namespace declaration |
|
//since scanAttributeValue doesn't use attIndex parameter therefore we |
|
|
|
XMLString tmpStr = getString(); |
|
|
|
scanAttributeValue(tmpStr, fTempString2, fAttributeQName.rawname, attributes, |
|
attIndex, isVC, fCurrentElement.rawname, false); |
|
|
|
|
|
int oldLen = attributes.getLength(); |
|
|
|
attIndex = attributes.addAttribute(fAttributeQName, XMLSymbols.fCDATASymbol, null); |
|
|
|
// WFC: Unique Att Spec |
|
//attributes count will be same if the current attribute name already exists for this element name. |
|
|
|
if (oldLen == attributes.getLength()) { |
|
reportFatalError("AttributeNotUnique", |
|
new Object[]{fCurrentElement.rawname, |
|
fAttributeQName.rawname}); |
|
} |
|
|
|
//tmpString contains attribute value |
|
|
|
attributes.setValue(attIndex, null, tmpStr); |
|
|
|
///xxx: nonNormalizedValue is not being set as it is not required by SAX & DOM |
|
|
|
attributes.setSpecified(attIndex, true); |
|
|
|
if (DEBUG_START_END_ELEMENT) System.out.println(this.getClass().toString() +"<<< scanAttribute()"); |
|
|
|
} // scanAttribute(XMLAttributes) |
|
|
|
/** |
|
* Scans element content. |
|
* |
|
* @return Returns the next character on the stream. |
|
*/ |
|
//CHANGED: |
|
//EARLIER: scanContent() |
|
//NOW: scanContent(XMLStringBuffer) |
|
//It makes things easy if this functions takes XMLStringBuffer as parameter.. |
|
|
|
protected int scanContent(XMLStringBuffer content) throws IOException, XNIException { |
|
//set the fTempString length to 0 before passing it on to scanContent |
|
|
|
fTempString.length = 0; |
|
int c = fEntityScanner.scanContent(fTempString); |
|
content.append(fTempString); |
|
fTempString.length = 0; |
|
if (c == '\r') { |
|
// happens when there is the character reference |
|
|
|
fEntityScanner.scanChar(null); |
|
content.append((char)c); |
|
c = -1; |
|
} else if (c == ']') { |
|
//fStringBuffer.clear(); |
|
|
|
content.append((char)fEntityScanner.scanChar(null)); |
|
// remember where we are in case we get an endEntity before we |
|
// could flush the buffer out - this happens when we're parsing an |
|
|
|
fInScanContent = true; |
|
// |
|
// We work on a single character basis to handle cases such as: |
|
// ']]]>' which we might otherwise miss. |
|
|
|
if (fEntityScanner.skipChar(']', null)) { |
|
content.append(']'); |
|
while (fEntityScanner.skipChar(']', null)) { |
|
content.append(']'); |
|
} |
|
if (fEntityScanner.skipChar('>', null)) { |
|
reportFatalError("CDEndInContent", null); |
|
} |
|
} |
|
fInScanContent = false; |
|
c = -1; |
|
} |
|
if (fDocumentHandler != null && content.length > 0) { |
|
//fDocumentHandler.characters(content, null); |
|
} |
|
return c; |
|
|
|
} // scanContent():int |
|
|
|
|
|
/** |
|
* Scans a CDATA section. |
|
* <p> |
|
* <strong>Note:</strong> This method uses the fTempString and |
|
* fStringBuffer variables. |
|
* |
|
* @param complete True if the CDATA section is to be scanned |
|
* completely. |
|
* |
|
* @return True if CDATA is completely scanned. |
|
*/ |
|
|
|
protected boolean scanCDATASection(XMLStringBuffer contentBuffer, boolean complete) |
|
throws IOException, XNIException { |
|
|
|
|
|
if (fDocumentHandler != null) { |
|
//fDocumentHandler.startCDATA(null); |
|
} |
|
|
|
while (true) { |
|
|
|
if (!fEntityScanner.scanData("]]>", contentBuffer, fChunkSize)) { |
|
fInCData = false; |
|
fCDataEnd = true; |
|
fMarkupDepth--; |
|
break ; |
|
} else { |
|
int c = fEntityScanner.peekChar(); |
|
if (c != -1 && isInvalidLiteral(c)) { |
|
if (XMLChar.isHighSurrogate(c)) { |
|
//contentBuffer.clear(); |
|
|
|
scanSurrogates(contentBuffer); |
|
} else { |
|
reportFatalError("InvalidCharInCDSect", |
|
new Object[]{Integer.toString(c,16)}); |
|
fEntityScanner.scanChar(null); |
|
} |
|
} else { |
|
|
|
fInCData = true; |
|
fCDataEnd = false; |
|
break; |
|
} |
|
|
|
if (fDocumentHandler != null) { |
|
//fDocumentHandler.characters(contentBuffer, null); |
|
} |
|
} |
|
} |
|
|
|
return true; |
|
|
|
} // scanCDATASection(XMLStringBuffer, boolean):boolean |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
protected int scanEndElement() throws IOException, XNIException { |
|
if (DEBUG_START_END_ELEMENT) System.out.println(this.getClass().toString() +">>> scanEndElement()"); |
|
|
|
|
|
QName endElementName = fElementStack.popElement(); |
|
|
|
String rawname = endElementName.rawname; |
|
if(DEBUG)System.out.println("endElementName = " + endElementName.toString()); |
|
// Take advantage of the fact that next string _should_ be "fElementQName.rawName", |
|
//In scanners most of the time is consumed on checks done for XML characters, we can |
|
// optimize on it and avoid the checks done for endElement, |
|
//we will also avoid symbol table lookup. |
|
|
|
// this should work both for namespace processing true or false... |
|
|
|
//REVISIT: if the string is not the same as expected.. we need to do better error handling.. |
|
//We can skip this for now... In any case if the string doesn't match -- document is not well formed. |
|
|
|
if (!fEntityScanner.skipString(endElementName.rawname)) { |
|
reportFatalError("ETagRequired", new Object[]{rawname}); |
|
} |
|
|
|
|
|
fEntityScanner.skipSpaces(); |
|
if (!fEntityScanner.skipChar('>', NameType.ELEMENTEND)) { |
|
reportFatalError("ETagUnterminated", |
|
new Object[]{rawname}); |
|
} |
|
fMarkupDepth--; |
|
|
|
|
|
fMarkupDepth--; |
|
|
|
|
|
if (fMarkupDepth < fEntityStack[fEntityDepth - 1]) { |
|
reportFatalError("ElementEntityMismatch", |
|
new Object[]{rawname}); |
|
} |
|
|
|
//We should not be popping out the context here in endELement becaause the namespace context is still |
|
//valid when parser is at the endElement state. |
|
|
|
//if (fNamespaces) { |
|
// fNamespaceContext.popContext(); |
|
//} |
|
|
|
|
|
if (fDocumentHandler != null ) { |
|
//end element is scanned in this function so we can send a callback |
|
//here. |
|
//<strong>we shouldn't be sending callback in scanDocument()</strong> |
|
|
|
fDocumentHandler.endElement(endElementName, null); |
|
} |
|
if(dtdGrammarUtil != null) |
|
dtdGrammarUtil.endElement(endElementName); |
|
|
|
return fMarkupDepth; |
|
|
|
} // scanEndElement():int |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
protected void scanCharReference() |
|
throws IOException, XNIException { |
|
|
|
fStringBuffer2.clear(); |
|
int ch = scanCharReferenceValue(fStringBuffer2, null); |
|
fMarkupDepth--; |
|
if (ch != -1) { |
|
// call handler |
|
|
|
if (fDocumentHandler != null) { |
|
if (fNotifyCharRefs) { |
|
fDocumentHandler.startGeneralEntity(fCharRefLiteral, null, null, null); |
|
} |
|
Augmentations augs = null; |
|
if (fValidation && ch <= 0x20) { |
|
if (fTempAugmentations != null) { |
|
fTempAugmentations.removeAllItems(); |
|
} |
|
else { |
|
fTempAugmentations = new AugmentationsImpl(); |
|
} |
|
augs = fTempAugmentations; |
|
augs.putItem(Constants.CHAR_REF_PROBABLE_WS, Boolean.TRUE); |
|
} |
|
//xxx: How do we deal with this - how to return charReferenceValues |
|
//now this is being commented because this is taken care in scanDocument() |
|
|
|
if (fNotifyCharRefs) { |
|
fDocumentHandler.endGeneralEntity(fCharRefLiteral, null); |
|
} |
|
} |
|
} |
|
|
|
} // scanCharReference() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
protected void scanEntityReference(XMLStringBuffer content) throws IOException, XNIException { |
|
String name = fEntityScanner.scanName(NameType.REFERENCE); |
|
if (name == null) { |
|
reportFatalError("NameRequiredInReference", null); |
|
return; |
|
} |
|
if (!fEntityScanner.skipChar(';', NameType.REFERENCE)) { |
|
reportFatalError("SemicolonRequiredInReference", new Object []{name}); |
|
} |
|
if (fEntityStore.isUnparsedEntity(name)) { |
|
reportFatalError("ReferenceToUnparsedEntity", new Object[]{name}); |
|
} |
|
fMarkupDepth--; |
|
fCurrentEntityName = name; |
|
|
|
|
|
if (name == fAmpSymbol) { |
|
handleCharacter('&', fAmpSymbol, content); |
|
fScannerState = SCANNER_STATE_BUILT_IN_REFS; |
|
return ; |
|
} else if (name == fLtSymbol) { |
|
handleCharacter('<', fLtSymbol, content); |
|
fScannerState = SCANNER_STATE_BUILT_IN_REFS; |
|
return ; |
|
} else if (name == fGtSymbol) { |
|
handleCharacter('>', fGtSymbol, content); |
|
fScannerState = SCANNER_STATE_BUILT_IN_REFS; |
|
return ; |
|
} else if (name == fQuotSymbol) { |
|
handleCharacter('"', fQuotSymbol, content); |
|
fScannerState = SCANNER_STATE_BUILT_IN_REFS; |
|
return ; |
|
} else if (name == fAposSymbol) { |
|
handleCharacter('\'', fAposSymbol, content); |
|
fScannerState = SCANNER_STATE_BUILT_IN_REFS; |
|
return ; |
|
} |
|
|
|
//1. if the entity is external and support to external entities is not required |
|
// 2. or entities should not be replaced |
|
|
|
boolean isEE = fEntityStore.isExternalEntity(name); |
|
if((isEE && !fSupportExternalEntities) || (!isEE && !fReplaceEntityReferences) || foundBuiltInRefs){ |
|
fScannerState = SCANNER_STATE_REFERENCE; |
|
return ; |
|
} |
|
|
|
if (!fEntityStore.isDeclaredEntity(name)) { |
|
|
|
if (!fSupportDTD && fReplaceEntityReferences) { |
|
reportFatalError("EntityNotDeclared", new Object[]{name}); |
|
return; |
|
} |
|
|
|
if ( fHasExternalDTD && !fStandalone) { |
|
if (fValidation) |
|
fErrorReporter.reportError(fEntityScanner, XMLMessageFormatter.XML_DOMAIN,"EntityNotDeclared", |
|
new Object[]{name}, XMLErrorReporter.SEVERITY_ERROR); |
|
} else |
|
reportFatalError("EntityNotDeclared", new Object[]{name}); |
|
} |
|
|
|
|
|
if (fCreateEntityRefNodes) { |
|
fDocumentHandler.startGeneralEntity(name, null, null, null); |
|
} else { |
|
//we are starting the entity even if the entity was not declared |
|
//if that was the case it its taken care in XMLEntityManager.startEntity() |
|
//we immediately call the endEntity. Application gets to know if there was |
|
|
|
fEntityManager.startEntity(true, name, false); |
|
//set the scaner state to content.. parser will automatically revive itself at any point of time. |
|
//setScannerState(SCANNER_STATE_CONTENT); |
|
//return true ; |
|
} |
|
} // scanEntityReference() |
|
|
|
// utility methods |
|
|
|
|
|
|
|
|
|
*/ |
|
void checkDepth(String elementName) { |
|
fLimitAnalyzer.addValue(Limit.MAX_ELEMENT_DEPTH_LIMIT, elementName, fElementStack.fDepth); |
|
if (fSecurityManager.isOverLimit(Limit.MAX_ELEMENT_DEPTH_LIMIT,fLimitAnalyzer)) { |
|
fSecurityManager.debugPrint(fLimitAnalyzer); |
|
reportFatalError("MaxElementDepthLimit", new Object[]{elementName, |
|
fLimitAnalyzer.getTotalValue(Limit.MAX_ELEMENT_DEPTH_LIMIT), |
|
fSecurityManager.getLimit(Limit.MAX_ELEMENT_DEPTH_LIMIT), |
|
"maxElementDepth"}); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private void handleCharacter(char c, String entity, XMLStringBuffer content) throws XNIException { |
|
foundBuiltInRefs = true; |
|
checkEntityLimit(false, fEntityScanner.fCurrentEntity.name, 1); |
|
content.append(c); |
|
if (fDocumentHandler != null) { |
|
fSingleChar[0] = c; |
|
if (fNotifyBuiltInRefs) { |
|
fDocumentHandler.startGeneralEntity(entity, null, null, null); |
|
} |
|
fTempString.setValues(fSingleChar, 0, 1); |
|
if(!fIsCoalesce){ |
|
fDocumentHandler.characters(fTempString, null); |
|
builtInRefCharacterHandled = true; |
|
} |
|
|
|
if (fNotifyBuiltInRefs) { |
|
fDocumentHandler.endGeneralEntity(entity, null); |
|
} |
|
} |
|
} // handleCharacter(char) |
|
|
|
// helper methods |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
protected final void setScannerState(int state) { |
|
|
|
fScannerState = state; |
|
if (DEBUG_SCANNER_STATE) { |
|
System.out.print("### setScannerState: "); |
|
|
|
System.out.print(getScannerStateName(state)); |
|
System.out.println(); |
|
} |
|
|
|
} // setScannerState(int) |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
protected final void setDriver(Driver driver) { |
|
fDriver = driver; |
|
if (DEBUG_DISPATCHER) { |
|
System.out.print("%%% setDriver: "); |
|
System.out.print(getDriverName(driver)); |
|
System.out.println(); |
|
} |
|
} |
|
|
|
// |
|
// Private methods |
|
// |
|
|
|
|
|
protected String getScannerStateName(int state) { |
|
|
|
switch (state) { |
|
case SCANNER_STATE_DOCTYPE: return "SCANNER_STATE_DOCTYPE"; |
|
case SCANNER_STATE_ROOT_ELEMENT: return "SCANNER_STATE_ROOT_ELEMENT"; |
|
case SCANNER_STATE_START_OF_MARKUP: return "SCANNER_STATE_START_OF_MARKUP"; |
|
case SCANNER_STATE_COMMENT: return "SCANNER_STATE_COMMENT"; |
|
case SCANNER_STATE_PI: return "SCANNER_STATE_PI"; |
|
case SCANNER_STATE_CONTENT: return "SCANNER_STATE_CONTENT"; |
|
case SCANNER_STATE_REFERENCE: return "SCANNER_STATE_REFERENCE"; |
|
case SCANNER_STATE_END_OF_INPUT: return "SCANNER_STATE_END_OF_INPUT"; |
|
case SCANNER_STATE_TERMINATED: return "SCANNER_STATE_TERMINATED"; |
|
case SCANNER_STATE_CDATA: return "SCANNER_STATE_CDATA"; |
|
case SCANNER_STATE_TEXT_DECL: return "SCANNER_STATE_TEXT_DECL"; |
|
case SCANNER_STATE_ATTRIBUTE: return "SCANNER_STATE_ATTRIBUTE"; |
|
case SCANNER_STATE_ATTRIBUTE_VALUE: return "SCANNER_STATE_ATTRIBUTE_VALUE"; |
|
case SCANNER_STATE_START_ELEMENT_TAG: return "SCANNER_STATE_START_ELEMENT_TAG"; |
|
case SCANNER_STATE_END_ELEMENT_TAG: return "SCANNER_STATE_END_ELEMENT_TAG"; |
|
case SCANNER_STATE_CHARACTER_DATA: return "SCANNER_STATE_CHARACTER_DATA" ; |
|
} |
|
|
|
return "??? ("+state+')'; |
|
|
|
} |
|
public String getEntityName(){ |
|
|
|
return fCurrentEntityName; |
|
} |
|
|
|
|
|
public String getDriverName(Driver driver) { |
|
|
|
if (DEBUG_DISPATCHER) { |
|
if (driver != null) { |
|
String name = driver.getClass().getName(); |
|
int index = name.lastIndexOf('.'); |
|
if (index != -1) { |
|
name = name.substring(index + 1); |
|
index = name.lastIndexOf('$'); |
|
if (index != -1) { |
|
name = name.substring(index + 1); |
|
} |
|
} |
|
return name; |
|
} |
|
} |
|
return "null"; |
|
|
|
} // getDriverName():String |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
String checkAccess(String systemId, String allowedProtocols) throws IOException { |
|
String baseSystemId = fEntityScanner.getBaseSystemId(); |
|
String expandedSystemId = XMLEntityManager.expandSystemId(systemId, baseSystemId, fStrictURI); |
|
return SecuritySupport.checkAccess(expandedSystemId, allowedProtocols, JdkConstants.ACCESS_EXTERNAL_ALL); |
|
} |
|
|
|
// |
|
// Classes |
|
// |
|
|
|
|
|
|
|
*/ |
|
protected static final class Element { |
|
|
|
// |
|
// Data |
|
// |
|
|
|
|
|
public QName qname; |
|
|
|
|
|
public char[] fRawname; |
|
|
|
|
|
public Element next; |
|
|
|
// |
|
// Constructors |
|
// |
|
|
|
|
|
|
|
|
|
*/ |
|
public Element(QName qname, Element next) { |
|
this.qname.setValues(qname); |
|
this.fRawname = qname.rawname.toCharArray(); |
|
this.next = next; |
|
} |
|
|
|
} // class Element |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
protected class ElementStack2 { |
|
|
|
// |
|
// Data |
|
// |
|
|
|
|
|
protected QName [] fQName = new QName[20]; |
|
|
|
|
|
protected int fDepth; |
|
|
|
protected int fCount; |
|
|
|
protected int fPosition; |
|
|
|
protected int fMark; |
|
|
|
protected int fLastDepth ; |
|
|
|
// |
|
// Constructors |
|
// |
|
|
|
|
|
public ElementStack2() { |
|
for (int i = 0; i < fQName.length; i++) { |
|
fQName[i] = new QName(); |
|
} |
|
fMark = fPosition = 1; |
|
} // <init>() |
|
|
|
public void resize(){ |
|
/** |
|
* int length = fElements.length; |
|
* Element [] temp = new Element[length * 2]; |
|
* System.arraycopy(fElements, 0, temp, 0, length); |
|
* fElements = temp; |
|
*/ |
|
|
|
int oldLength = fQName.length; |
|
QName [] tmp = new QName[oldLength * 2]; |
|
System.arraycopy(fQName, 0, tmp, 0, oldLength); |
|
fQName = tmp; |
|
|
|
for (int i = oldLength; i < fQName.length; i++) { |
|
fQName[i] = new QName(); |
|
} |
|
|
|
} |
|
|
|
|
|
// |
|
// Public methods |
|
// |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public boolean matchElement(QName element) { |
|
//last depth is the depth when last elemnt was pushed |
|
|
|
if(DEBUG_SKIP_ALGORITHM){ |
|
System.out.println("fLastDepth = " + fLastDepth); |
|
System.out.println("fDepth = " + fDepth); |
|
} |
|
boolean match = false; |
|
if(fLastDepth > fDepth && fDepth <= 2){ |
|
if(DEBUG_SKIP_ALGORITHM){ |
|
System.out.println("Checking if the elements match " + element.rawname + " , " + fQName[fDepth].rawname); |
|
} |
|
if(element.rawname == fQName[fDepth].rawname){ |
|
fAdd = false; |
|
//mark this position |
|
|
|
fMark = fDepth - 1; |
|
|
|
fPosition = fMark + 1 ; |
|
match = true; |
|
|
|
--fCount; |
|
if(DEBUG_SKIP_ALGORITHM){ |
|
System.out.println("fAdd FALSE -- NOW ELEMENT SHOULD NOT BE ADDED"); |
|
System.out.println("fMark = " + fMark); |
|
System.out.println("fPosition = " + fPosition); |
|
System.out.println("fDepth = " + fDepth); |
|
System.out.println("fCount = " + fCount); |
|
} |
|
}else{ |
|
fAdd = true; |
|
if(DEBUG_SKIP_ALGORITHM)System.out.println("fAdd is " + fAdd); |
|
} |
|
} |
|
|
|
fLastDepth = fDepth++; |
|
return match; |
|
} // pushElement(QName):QName |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public QName nextElement() { |
|
|
|
|
|
if (fCount == fQName.length) { |
|
fShouldSkip = false; |
|
fAdd = false; |
|
if(DEBUG_SKIP_ALGORITHM)System.out.println("SKIPPING STOPPED, fShouldSkip = " + fShouldSkip); |
|
//xxx: this is not correct, we are returning the last element |
|
|
|
return fQName[--fCount]; |
|
} |
|
if(DEBUG_SKIP_ALGORITHM){ |
|
System.out.println("fCount = " + fCount); |
|
} |
|
return fQName[fCount++]; |
|
|
|
} |
|
|
|
|
|
|
|
*/ |
|
public QName getNext(){ |
|
//when position reaches number of elements in the list.. |
|
|
|
if(fPosition == fCount){ |
|
fPosition = fMark; |
|
} |
|
return fQName[fPosition++]; |
|
} |
|
|
|
|
|
*/ |
|
public int popElement(){ |
|
return fDepth--; |
|
} |
|
|
|
|
|
|
|
public void clear() { |
|
fLastDepth = 0; |
|
fDepth = 0; |
|
fCount = 0 ; |
|
fPosition = fMark = 1; |
|
} // clear() |
|
|
|
} // class ElementStack |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
protected class ElementStack { |
|
|
|
// |
|
// Data |
|
// |
|
|
|
|
|
protected QName[] fElements; |
|
protected int [] fInt = new int[20]; |
|
|
|
|
|
|
|
protected int fDepth; |
|
|
|
protected int fCount; |
|
|
|
protected int fPosition; |
|
|
|
protected int fMark; |
|
|
|
protected int fLastDepth ; |
|
|
|
// |
|
// Constructors |
|
// |
|
|
|
|
|
public ElementStack() { |
|
fElements = new QName[20]; |
|
for (int i = 0; i < fElements.length; i++) { |
|
fElements[i] = new QName(); |
|
} |
|
} // <init>() |
|
|
|
// |
|
// Public methods |
|
// |
|
|
|
/** |
|
* Pushes an element on the stack. |
|
* <p> |
|
* <strong>Note:</strong> The QName values are copied into the |
|
* stack. In other words, the caller does <em>not</em> orphan |
|
* the element to the stack. Also, the QName object returned |
|
* is <em>not</em> orphaned to the caller. It should be |
|
* considered read-only. |
|
* |
|
* @param element The element to push onto the stack. |
|
* |
|
* @return Returns the actual QName object that stores the |
|
*/ |
|
|
|
public QName pushElement(QName element) { |
|
if (fDepth == fElements.length) { |
|
QName[] array = new QName[fElements.length * 2]; |
|
System.arraycopy(fElements, 0, array, 0, fDepth); |
|
fElements = array; |
|
for (int i = fDepth; i < fElements.length; i++) { |
|
fElements[i] = new QName(); |
|
} |
|
} |
|
fElements[fDepth].setValues(element); |
|
return fElements[fDepth++]; |
|
} // pushElement(QName):QName |
|
|
|
|
|
|
|
|
|
*/ |
|
public QName getNext(){ |
|
//when position reaches number of elements in the list.. |
|
|
|
if(fPosition == fCount){ |
|
fPosition = fMark; |
|
} |
|
//store the position of last opened tag at particular depth |
|
|
|
if(DEBUG_SKIP_ALGORITHM){ |
|
System.out.println("Element at fPosition = " + fPosition + " is " + fElements[fPosition].rawname); |
|
} |
|
|
|
return fElements[fPosition]; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public void push(){ |
|
|
|
fInt[++fDepth] = fPosition++; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public boolean matchElement(QName element) { |
|
//last depth is the depth when last elemnt was pushed |
|
//if last depth is greater than current depth |
|
//if(DEBUG_SKIP_ALGORITHM){ |
|
// System.out.println("Check if the element " + element.rawname + " matches"); |
|
// System.out.println("fLastDepth = " + fLastDepth); |
|
// System.out.println("fDepth = " + fDepth); |
|
|
|
boolean match = false; |
|
if(fLastDepth > fDepth && fDepth <= 3){ |
|
if(DEBUG_SKIP_ALGORITHM){ |
|
System.out.println("----------ENTERED THE LOOP WHERE WE CHECK FOR MATCHING OF ELMENT-----"); |
|
System.out.println("Depth = " + fDepth + " Checking if INCOMING element " + element.rawname + " match STORED ELEMENT " + fElements[fDepth - 1].rawname); |
|
} |
|
if(element.rawname == fElements[fDepth - 1].rawname){ |
|
fAdd = false; |
|
//mark this position |
|
|
|
fMark = fDepth - 1; |
|
|
|
fPosition = fMark; |
|
match = true; |
|
|
|
--fCount; |
|
if(DEBUG_SKIP_ALGORITHM){ |
|
System.out.println("NOW ELEMENT SHOULD NOT BE ADDED, fAdd is set to false"); |
|
System.out.println("fMark = " + fMark); |
|
System.out.println("fPosition = " + fPosition); |
|
System.out.println("fDepth = " + fDepth); |
|
System.out.println("fCount = " + fCount); |
|
System.out.println("---------MATCH SUCEEDED-----------------"); |
|
System.out.println(""); |
|
} |
|
}else{ |
|
fAdd = true; |
|
if(DEBUG_SKIP_ALGORITHM)System.out.println("fAdd is " + fAdd); |
|
} |
|
} |
|
//store the position for the current depth |
|
//when we are adding the elements, when skipping |
|
//starts even then this should be tracked ie. when |
|
|
|
if(match){ |
|
|
|
fInt[fDepth] = fPosition++; |
|
} else{ |
|
if(DEBUG_SKIP_ALGORITHM){ |
|
System.out.println("At depth = " + fDepth + "array position is = " + (fCount - 1)); |
|
} |
|
|
|
fInt[fDepth] = fCount - 1; |
|
} |
|
|
|
//if number of elements becomes equal to the length of array -- stop the skipping |
|
|
|
if (fCount == fElements.length) { |
|
fSkip = false; |
|
fAdd = false; |
|
|
|
reposition(); |
|
if(DEBUG_SKIP_ALGORITHM){ |
|
System.out.println("ALL THE ELMENTS IN ARRAY HAVE BEEN FILLED"); |
|
System.out.println("REPOSITIONING THE STACK"); |
|
System.out.println("-----------SKIPPING STOPPED----------"); |
|
System.out.println(""); |
|
} |
|
return false; |
|
} |
|
if(DEBUG_SKIP_ALGORITHM){ |
|
if(match){ |
|
System.out.println("Storing fPosition = " + fInt[fDepth] + " at fDepth = " + fDepth); |
|
}else{ |
|
System.out.println("Storing fCount = " + fInt[fDepth] + " at fDepth = " + fDepth); |
|
} |
|
} |
|
|
|
fLastDepth = fDepth; |
|
return match; |
|
} // matchElement(QName):QName |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public QName nextElement() { |
|
if(fSkip){ |
|
fDepth++; |
|
|
|
return fElements[fCount++]; |
|
} else if (fDepth == fElements.length) { |
|
QName[] array = new QName[fElements.length * 2]; |
|
System.arraycopy(fElements, 0, array, 0, fDepth); |
|
fElements = array; |
|
for (int i = fDepth; i < fElements.length; i++) { |
|
fElements[i] = new QName(); |
|
} |
|
} |
|
|
|
return fElements[fDepth++]; |
|
|
|
} // pushElement(QName):QName |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public QName popElement() { |
|
//return the same object that was pushed -- this would avoid |
|
//setting the values for every end element. |
|
|
|
if(fSkip || fAdd ){ |
|
if(DEBUG_SKIP_ALGORITHM){ |
|
System.out.println("POPPING Element, at position " + fInt[fDepth] + " element at that count is = " + fElements[fInt[fDepth]].rawname); |
|
System.out.println(""); |
|
} |
|
return fElements[fInt[fDepth--]]; |
|
} else{ |
|
if(DEBUG_SKIP_ALGORITHM){ |
|
System.out.println("Retrieveing element at depth = " + fDepth + " is " + fElements[fDepth].rawname ); |
|
} |
|
return fElements[--fDepth] ; |
|
} |
|
//element.setValues(fElements[--fDepth]); |
|
} // popElement(QName) |
|
|
|
|
|
|
|
|
|
*/ |
|
public void reposition(){ |
|
for( int i = 2 ; i <= fDepth ; i++){ |
|
fElements[i-1] = fElements[fInt[i]]; |
|
} |
|
if(DEBUG_SKIP_ALGORITHM){ |
|
for( int i = 0 ; i < fDepth ; i++){ |
|
System.out.println("fElements[" + i + "]" + " = " + fElements[i].rawname); |
|
} |
|
} |
|
} |
|
|
|
|
|
public void clear() { |
|
fDepth = 0; |
|
fLastDepth = 0; |
|
fCount = 0 ; |
|
fPosition = fMark = 1; |
|
|
|
} // clear() |
|
|
|
/** |
|
* This function is as a result of optimization done for endElement -- |
|
* we dont need to set the value for every end element encouterd. |
|
* For Well formedness checks we can have the same QName object that was pushed. |
|
* the values will be set only if application need to know about the endElement |
|
*/ |
|
|
|
public QName getLastPoppedElement(){ |
|
return fElements[fDepth]; |
|
} |
|
} // class ElementStack |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
protected interface Driver { |
|
|
|
|
|
/** |
|
* Drives the parser to the next state/event on the input. Parser is guaranteed |
|
* to stop at the next state/event. |
|
* |
|
* Internally XML document is divided into several states. Each state represents |
|
* a sections of XML document. When this functions returns normally, it has read |
|
* the section of XML document and returns the state corresponding to section of |
|
* document which has been read. For optimizations, a particular driver |
|
* can read ahead of the section of document (state returned) just read and |
|
* can maintain a different internal state. |
|
* |
|
* @return state representing the section of document just read. |
|
* |
|
* @throws IOException Thrown on i/o error. |
|
* @throws XNIException Thrown on parse error. |
|
*/ |
|
|
|
public int next() throws IOException, XNIException; |
|
|
|
} // interface Driver |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
protected class FragmentContentDriver |
|
implements Driver { |
|
|
|
// |
|
// Driver methods |
|
// |
|
|
|
|
|
|
|
*/ |
|
private void startOfMarkup() throws IOException { |
|
fMarkupDepth++; |
|
final int ch = fEntityScanner.peekChar(); |
|
if (isValidNameStartChar(ch) || isValidNameStartHighSurrogate(ch)) { |
|
setScannerState(SCANNER_STATE_START_ELEMENT_TAG); |
|
} else { |
|
switch(ch){ |
|
case '?' :{ |
|
setScannerState(SCANNER_STATE_PI); |
|
fEntityScanner.skipChar(ch, null); |
|
break; |
|
} |
|
case '!' :{ |
|
fEntityScanner.skipChar(ch, null); |
|
if (fEntityScanner.skipChar('-', null)) { |
|
if (!fEntityScanner.skipChar('-', NameType.COMMENT)) { |
|
reportFatalError("InvalidCommentStart", |
|
null); |
|
} |
|
setScannerState(SCANNER_STATE_COMMENT); |
|
} else if (fEntityScanner.skipString(CDATA)) { |
|
fCDataStart = true; |
|
setScannerState(SCANNER_STATE_CDATA ); |
|
} else if (!scanForDoctypeHook()) { |
|
reportFatalError("MarkupNotRecognizedInContent", |
|
null); |
|
} |
|
break; |
|
} |
|
case '/' :{ |
|
setScannerState(SCANNER_STATE_END_ELEMENT_TAG); |
|
fEntityScanner.skipChar(ch, NameType.ELEMENTEND); |
|
break; |
|
} |
|
default :{ |
|
reportFatalError("MarkupNotRecognizedInContent", null); |
|
} |
|
} |
|
} |
|
|
|
}//startOfMarkup |
|
|
|
private void startOfContent() throws IOException { |
|
if (fEntityScanner.skipChar('<', null)) { |
|
setScannerState(SCANNER_STATE_START_OF_MARKUP); |
|
} else if (fEntityScanner.skipChar('&', NameType.REFERENCE)) { |
|
setScannerState(SCANNER_STATE_REFERENCE) ; |
|
} else { |
|
|
|
setScannerState(SCANNER_STATE_CHARACTER_DATA); |
|
} |
|
}//startOfContent |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public void decideSubState() throws IOException { |
|
while( fScannerState == SCANNER_STATE_CONTENT || fScannerState == SCANNER_STATE_START_OF_MARKUP){ |
|
|
|
switch (fScannerState) { |
|
|
|
case SCANNER_STATE_CONTENT: { |
|
startOfContent() ; |
|
break; |
|
} |
|
|
|
case SCANNER_STATE_START_OF_MARKUP: { |
|
startOfMarkup() ; |
|
break; |
|
} |
|
} |
|
} |
|
}//decideSubState |
|
|
|
/** |
|
* Drives the parser to the next state/event on the input. Parser is guaranteed |
|
* to stop at the next state/event. Internally XML document |
|
* is divided into several states. Each state represents a sections of XML |
|
* document. When this functions returns normally, it has read the section |
|
* of XML document and returns the state corresponding to section of |
|
* document which has been read. For optimizations, a particular driver |
|
* can read ahead of the section of document (state returned) just read and |
|
* can maintain a different internal state. |
|
* |
|
* State returned corresponds to Stax states. |
|
* |
|
* @return state representing the section of document just read. |
|
* |
|
* @throws IOException Thrown on i/o error. |
|
* @throws XNIException Thrown on parse error. |
|
*/ |
|
|
|
public int next() throws IOException, XNIException { |
|
while (true) { |
|
try { |
|
|
|
//decide the actual sub state of the scanner.For more information refer to the javadoc of |
|
//decideSubState. |
|
|
|
if (fScannerState == SCANNER_STATE_CONTENT) { |
|
final int ch = fEntityScanner.peekChar(); |
|
if (ch == '<') { |
|
fEntityScanner.scanChar(null); |
|
setScannerState(SCANNER_STATE_START_OF_MARKUP); |
|
} else if (ch == '&') { |
|
fEntityScanner.scanChar(NameType.REFERENCE); |
|
setScannerState(SCANNER_STATE_REFERENCE) ; |
|
} else { |
|
|
|
setScannerState(SCANNER_STATE_CHARACTER_DATA); |
|
} |
|
} |
|
|
|
if (fScannerState == SCANNER_STATE_START_OF_MARKUP) { |
|
startOfMarkup(); |
|
} |
|
|
|
//decideSubState() ; |
|
|
|
|
|
if (fIsCoalesce) { |
|
fUsebuffer = true ; |
|
|
|
if (fLastSectionWasCharacterData) { |
|
|
|
//if we dont encounter any CDATA or ENTITY REFERENCE and |
|
//current state is also not SCANNER_STATE_CHARACTER_DATA |
|
|
|
if ((fScannerState != SCANNER_STATE_CDATA) |
|
&& (fScannerState != SCANNER_STATE_REFERENCE) |
|
&& (fScannerState != SCANNER_STATE_CHARACTER_DATA)) { |
|
fLastSectionWasCharacterData = false; |
|
return XMLEvent.CHARACTERS; |
|
} |
|
}//if last section was CDATA or ENTITY REFERENCE |
|
//xxx: there might be another entity reference or CDATA after this |
|
|
|
else if ((fLastSectionWasCData || fLastSectionWasEntityReference)) { |
|
//and current state is not SCANNER_STATE_CHARACTER_DATA |
|
//or SCANNER_STATE_CDATA or SCANNER_STATE_REFERENCE |
|
//this means there is nothing more to be coalesced. |
|
|
|
if ((fScannerState != SCANNER_STATE_CDATA) |
|
&& (fScannerState != SCANNER_STATE_REFERENCE) |
|
&& (fScannerState != SCANNER_STATE_CHARACTER_DATA)){ |
|
|
|
fLastSectionWasCData = false; |
|
fLastSectionWasEntityReference = false; |
|
return XMLEvent.CHARACTERS; |
|
} |
|
} |
|
} |
|
|
|
switch(fScannerState){ |
|
|
|
case XMLEvent.START_DOCUMENT : |
|
return XMLEvent.START_DOCUMENT; |
|
|
|
case SCANNER_STATE_START_ELEMENT_TAG :{ |
|
|
|
|
|
fEmptyElement = scanStartElement() ; |
|
|
|
if(fEmptyElement){ |
|
setScannerState(SCANNER_STATE_END_ELEMENT_TAG); |
|
}else{ |
|
|
|
setScannerState(SCANNER_STATE_CONTENT); |
|
} |
|
return XMLEvent.START_ELEMENT ; |
|
} |
|
|
|
case SCANNER_STATE_CHARACTER_DATA: { |
|
|
|
//if last section was either entity reference or cdata or |
|
|
|
fUsebuffer = fLastSectionWasEntityReference || fLastSectionWasCData |
|
|| fLastSectionWasCharacterData ; |
|
|
|
//When coalesce is set to true and last state was REFERENCE or |
|
|
|
if( fIsCoalesce && (fLastSectionWasEntityReference || |
|
fLastSectionWasCData || fLastSectionWasCharacterData) ){ |
|
fLastSectionWasEntityReference = false; |
|
fLastSectionWasCData = false; |
|
fLastSectionWasCharacterData = true ; |
|
fUsebuffer = true; |
|
}else{ |
|
|
|
fContentBuffer.clear(); |
|
} |
|
|
|
//set the fTempString length to 0 before passing it on to scanContent |
|
|
|
fTempString.length = 0; |
|
int c = fEntityScanner.scanContent(fTempString); |
|
|
|
if(fEntityScanner.skipChar('<', null)){ |
|
|
|
if(fEntityScanner.skipChar('/', NameType.ELEMENTEND)){ |
|
|
|
fMarkupDepth++; |
|
fLastSectionWasCharacterData = false; |
|
setScannerState(SCANNER_STATE_END_ELEMENT_TAG); |
|
//check if its start of new element |
|
}else if(XMLChar.isNameStart(fEntityScanner.peekChar())){ |
|
fMarkupDepth++; |
|
fLastSectionWasCharacterData = false; |
|
setScannerState(SCANNER_STATE_START_ELEMENT_TAG); |
|
}else{ |
|
setScannerState(SCANNER_STATE_START_OF_MARKUP); |
|
|
|
if(fIsCoalesce){ |
|
fLastSectionWasCharacterData = true; |
|
bufferContent(); |
|
continue; |
|
} |
|
} |
|
//in case last section was either entity reference or |
|
|
|
if(fUsebuffer){ |
|
bufferContent(); |
|
} |
|
|
|
if(dtdGrammarUtil!= null && dtdGrammarUtil.isIgnorableWhiteSpace(fContentBuffer)){ |
|
if(DEBUG)System.out.println("Return SPACE EVENT"); |
|
return XMLEvent.SPACE; |
|
}else |
|
return XMLEvent.CHARACTERS; |
|
|
|
} else{ |
|
bufferContent(); |
|
} |
|
if (c == '\r') { |
|
if(DEBUG){ |
|
System.out.println("'\r' character found"); |
|
} |
|
// happens when there is the character reference |
|
|
|
fEntityScanner.scanChar(null); |
|
fUsebuffer = true; |
|
fContentBuffer.append((char)c); |
|
c = -1 ; |
|
} else if (c == ']') { |
|
//fStringBuffer.clear(); |
|
|
|
fUsebuffer = true; |
|
fContentBuffer.append((char)fEntityScanner.scanChar(null)); |
|
// remember where we are in case we get an endEntity before we |
|
// could flush the buffer out - this happens when we're parsing an |
|
|
|
fInScanContent = true; |
|
|
|
// We work on a single character basis to handle cases such as: |
|
// ']]]>' which we might otherwise miss. |
|
|
|
if (fEntityScanner.skipChar(']', null)) { |
|
fContentBuffer.append(']'); |
|
while (fEntityScanner.skipChar(']', null)) { |
|
fContentBuffer.append(']'); |
|
} |
|
if (fEntityScanner.skipChar('>', null)) { |
|
reportFatalError("CDEndInContent", null); |
|
} |
|
} |
|
c = -1 ; |
|
fInScanContent = false; |
|
} |
|
|
|
do{ |
|
//xxx: we should be using only one buffer.. |
|
// we need not to grow the buffer only when isCoalesce() is not true; |
|
|
|
if (c == '<') { |
|
fEntityScanner.scanChar(null); |
|
setScannerState(SCANNER_STATE_START_OF_MARKUP); |
|
break; |
|
} |
|
else if (c == '&') { |
|
fEntityScanner.scanChar(NameType.REFERENCE); |
|
setScannerState(SCANNER_STATE_REFERENCE); |
|
break; |
|
} |
|
else if (c != -1 && isInvalidLiteral(c)) { |
|
if (XMLChar.isHighSurrogate(c)) { |
|
|
|
scanSurrogates(fContentBuffer) ; |
|
setScannerState(SCANNER_STATE_CONTENT); |
|
} else { |
|
reportFatalError("InvalidCharInContent", |
|
new Object[] { |
|
Integer.toString(c, 16)}); |
|
fEntityScanner.scanChar(null); |
|
} |
|
break; |
|
} |
|
|
|
c = scanContent(fContentBuffer) ; |
|
//we should not be iterating again if fIsCoalesce is not set to true |
|
|
|
if(!fIsCoalesce){ |
|
setScannerState(SCANNER_STATE_CONTENT); |
|
break; |
|
} |
|
|
|
}while(true); |
|
|
|
//if (fDocumentHandler != null) { |
|
// fDocumentHandler.characters(fContentBuffer, null); |
|
|
|
if(DEBUG)System.out.println("USING THE BUFFER, STRING START=" + fContentBuffer.toString() +"=END"); |
|
|
|
if(fIsCoalesce){ |
|
fLastSectionWasCharacterData = true ; |
|
continue; |
|
}else{ |
|
if(dtdGrammarUtil!= null && dtdGrammarUtil.isIgnorableWhiteSpace(fContentBuffer)){ |
|
if(DEBUG)System.out.println("Return SPACE EVENT"); |
|
return XMLEvent.SPACE; |
|
} else |
|
return XMLEvent.CHARACTERS ; |
|
} |
|
} |
|
|
|
case SCANNER_STATE_END_ELEMENT_TAG :{ |
|
if(fEmptyElement){ |
|
|
|
fEmptyElement = false; |
|
setScannerState(SCANNER_STATE_CONTENT); |
|
//check the case when there is comment after single element document |
|
|
|
return (fMarkupDepth == 0 && elementDepthIsZeroHook() ) ? |
|
XMLEvent.END_ELEMENT : XMLEvent.END_ELEMENT ; |
|
|
|
} else if(scanEndElement() == 0) { |
|
|
|
if (elementDepthIsZeroHook()) { |
|
//if element depth is zero , it indicates the end of the document |
|
//the state shouldn't be set, because it is set by elementDepthIsZeroHook() function |
|
|
|
return XMLEvent.END_ELEMENT ; |
|
} |
|
|
|
} |
|
setScannerState(SCANNER_STATE_CONTENT); |
|
return XMLEvent.END_ELEMENT ; |
|
} |
|
|
|
case SCANNER_STATE_COMMENT: { |
|
scanComment(); |
|
setScannerState(SCANNER_STATE_CONTENT); |
|
return XMLEvent.COMMENT; |
|
//break; |
|
} |
|
case SCANNER_STATE_PI:{ //SCANNER_STATE_PI: { |
|
|
|
fContentBuffer.clear() ; |
|
//xxx: which buffer should be passed. Ideally we shouldn't have |
|
//more than two buffers -- |
|
|
|
scanPI(fContentBuffer); |
|
setScannerState(SCANNER_STATE_CONTENT); |
|
return XMLEvent.PROCESSING_INSTRUCTION; |
|
//break; |
|
} |
|
case SCANNER_STATE_CDATA :{ //SCANNER_STATE_CDATA: { |
|
//xxx: What if CDATA is the first event |
|
//<foo><![CDATA[hello<><>]]>append</foo> |
|
|
|
//we should not clear the buffer only when the last state was |
|
//either SCANNER_STATE_REFERENCE or |
|
|
|
if(fIsCoalesce && ( fLastSectionWasEntityReference || |
|
fLastSectionWasCData || fLastSectionWasCharacterData)){ |
|
fLastSectionWasCData = true ; |
|
fLastSectionWasEntityReference = false; |
|
fLastSectionWasCharacterData = false; |
|
} |
|
else{ |
|
fContentBuffer.clear(); |
|
} |
|
fUsebuffer = true; |
|
|
|
scanCDATASection(fContentBuffer , true); |
|
if (!fCDataEnd) { |
|
setScannerState(SCANNER_STATE_CDATA); |
|
} else { |
|
setScannerState(SCANNER_STATE_CONTENT); |
|
} |
|
//1. if fIsCoalesce is set to true we set the variable fLastSectionWasCData to true |
|
//and just call fDispatche.next(). Since we have set the scanner state to |
|
//SCANNER_STATE_CONTENT (super state) parser will automatically recover and |
|
//behave appropriately. When isCoalesce is set to true we dont need to reportCDATA event |
|
//2. Check if application has set for reporting CDATA event |
|
//3. if the application has neither set the fIsCoalesce to true nor fReportCdataEvent |
|
|
|
if (fIsCoalesce) { |
|
fLastSectionWasCData = true ; |
|
|
|
continue; |
|
} else if(fReportCdataEvent) { |
|
return XMLEvent.CDATA; |
|
} else { |
|
return XMLEvent.CHARACTERS; |
|
} |
|
} |
|
|
|
case SCANNER_STATE_REFERENCE :{ |
|
fMarkupDepth++; |
|
foundBuiltInRefs = false; |
|
|
|
//we should not clear the buffer only when the last state was |
|
//either CDATA or |
|
|
|
if(fIsCoalesce && ( fLastSectionWasEntityReference || |
|
fLastSectionWasCData || fLastSectionWasCharacterData)){ |
|
//fLastSectionWasEntityReference or fLastSectionWasCData are only |
|
|
|
fLastSectionWasEntityReference = true ; |
|
fLastSectionWasCData = false; |
|
fLastSectionWasCharacterData = false; |
|
} |
|
else{ |
|
fContentBuffer.clear(); |
|
} |
|
fUsebuffer = true ; |
|
|
|
if (fEntityScanner.skipChar('#', NameType.REFERENCE)) { |
|
scanCharReferenceValue(fContentBuffer, null); |
|
fMarkupDepth--; |
|
if(!fIsCoalesce){ |
|
setScannerState(SCANNER_STATE_CONTENT); |
|
return XMLEvent.CHARACTERS; |
|
} |
|
} else { |
|
|
|
scanEntityReference(fContentBuffer); |
|
//if there was built-in entity reference & coalesce is not true |
|
|
|
if(fScannerState == SCANNER_STATE_BUILT_IN_REFS && !fIsCoalesce){ |
|
setScannerState(SCANNER_STATE_CONTENT); |
|
if (builtInRefCharacterHandled) { |
|
builtInRefCharacterHandled = false; |
|
return XMLEvent.ENTITY_REFERENCE; |
|
} else { |
|
return XMLEvent.CHARACTERS; |
|
} |
|
} |
|
|
|
|
|
if(fScannerState == SCANNER_STATE_TEXT_DECL){ |
|
fLastSectionWasEntityReference = true ; |
|
continue; |
|
} |
|
|
|
if(fScannerState == SCANNER_STATE_REFERENCE){ |
|
setScannerState(SCANNER_STATE_CONTENT); |
|
if (fReplaceEntityReferences && |
|
fEntityStore.isDeclaredEntity(fCurrentEntityName)) { |
|
|
|
continue; |
|
} |
|
return XMLEvent.ENTITY_REFERENCE; |
|
} |
|
} |
|
//Whether it was character reference, entity reference or built-in entity |
|
|
|
setScannerState(SCANNER_STATE_CONTENT); |
|
fLastSectionWasEntityReference = true ; |
|
continue; |
|
} |
|
|
|
case SCANNER_STATE_TEXT_DECL: { |
|
|
|
if (fEntityScanner.skipString("<?xml")) { |
|
fMarkupDepth++; |
|
// NOTE: special case where entity starts with a PI |
|
|
|
if (isValidNameChar(fEntityScanner.peekChar())) { |
|
fStringBuffer.clear(); |
|
fStringBuffer.append("xml"); |
|
|
|
if (fNamespaces) { |
|
while (isValidNCName(fEntityScanner.peekChar())) { |
|
fStringBuffer.append((char)fEntityScanner.scanChar(null)); |
|
} |
|
} else { |
|
while (isValidNameChar(fEntityScanner.peekChar())) { |
|
fStringBuffer.append((char)fEntityScanner.scanChar(null)); |
|
} |
|
} |
|
String target = fSymbolTable.addSymbol(fStringBuffer.ch, |
|
fStringBuffer.offset, fStringBuffer.length); |
|
fContentBuffer.clear(); |
|
scanPIData(target, fContentBuffer); |
|
} |
|
|
|
|
|
else { |
|
|
|
scanXMLDeclOrTextDecl(true); |
|
} |
|
} |
|
|
|
fEntityManager.fCurrentEntity.mayReadChunks = true; |
|
setScannerState(SCANNER_STATE_CONTENT); |
|
//xxx: we don't return any state, so how do we get to know about TEXT declarations. |
|
//it seems we have to careful when to allow function issue a callback |
|
|
|
continue; |
|
} |
|
|
|
|
|
case SCANNER_STATE_ROOT_ELEMENT: { |
|
if (scanRootElementHook()) { |
|
fEmptyElement = true; |
|
|
|
return XMLEvent.START_ELEMENT; |
|
} |
|
setScannerState(SCANNER_STATE_CONTENT); |
|
return XMLEvent.START_ELEMENT ; |
|
} |
|
case SCANNER_STATE_CHAR_REFERENCE : { |
|
fContentBuffer.clear(); |
|
scanCharReferenceValue(fContentBuffer, null); |
|
fMarkupDepth--; |
|
setScannerState(SCANNER_STATE_CONTENT); |
|
return XMLEvent.CHARACTERS; |
|
} |
|
default: |
|
throw new XNIException("Scanner State " + fScannerState + " not Recognized "); |
|
|
|
}//switch |
|
} |
|
|
|
catch (MalformedByteSequenceException e) { |
|
fErrorReporter.reportError(e.getDomain(), e.getKey(), |
|
e.getArguments(), XMLErrorReporter.SEVERITY_FATAL_ERROR, e); |
|
return -1; |
|
} |
|
catch (CharConversionException e) { |
|
fErrorReporter.reportError( |
|
XMLMessageFormatter.XML_DOMAIN, |
|
"CharConversionFailure", |
|
null, |
|
XMLErrorReporter.SEVERITY_FATAL_ERROR, e); |
|
return -1; |
|
} |
|
|
|
catch (EOFException e) { |
|
endOfFileHook(e); |
|
return -1; |
|
} |
|
} //while loop |
|
}//next |
|
|
|
// |
|
// Protected methods |
|
// |
|
|
|
// hooks |
|
|
|
// NOTE: These hook methods are added so that the full document |
|
// scanner can share the majority of code with this class. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
protected boolean scanForDoctypeHook() |
|
throws IOException, XNIException { |
|
return false; |
|
} // scanForDoctypeHook():boolean |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
protected boolean elementDepthIsZeroHook() |
|
throws IOException, XNIException { |
|
return false; |
|
} // elementDepthIsZeroHook():boolean |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
protected boolean scanRootElementHook() |
|
throws IOException, XNIException { |
|
return false; |
|
} // scanRootElementHook():boolean |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
protected void endOfFileHook(EOFException e) |
|
throws IOException, XNIException { |
|
|
|
// NOTE: An end of file is only only an error if we were |
|
|
|
if (fMarkupDepth != 0) { |
|
reportFatalError("PrematureEOF", null); |
|
} |
|
|
|
} // endOfFileHook() |
|
|
|
} // class FragmentContentDriver |
|
|
|
static void pr(String str) { |
|
System.out.println(str) ; |
|
} |
|
|
|
protected boolean fUsebuffer ; |
|
|
|
/** this function gets an XMLString (which is used to store the attribute value) from the special pool |
|
* maintained for attributes. |
|
* fAttributeCacheUsedCount tracks the number of attributes that has been consumed from the pool. |
|
* if all the attributes has been consumed, it adds a new XMLString inthe pool and returns the same |
|
* XMLString. |
|
* |
|
* @return XMLString XMLString used to store an attribute value. |
|
*/ |
|
|
|
protected XMLString getString(){ |
|
if(fAttributeCacheUsedCount < initialCacheCount || |
|
fAttributeCacheUsedCount < attributeValueCache.size()){ |
|
return attributeValueCache.get(fAttributeCacheUsedCount++); |
|
} else{ |
|
XMLString str = new XMLString(); |
|
fAttributeCacheUsedCount++; |
|
attributeValueCache.add(str); |
|
return str; |
|
} |
|
} |
|
|
|
/** |
|
* Implements XMLBufferListener interface. |
|
*/ |
|
|
|
public void refresh(){ |
|
refresh(0); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public void refresh(int refreshPosition){ |
|
//If you are reading attributes and you got a callback |
|
|
|
if(fReadingAttributes){ |
|
fAttributes.refresh(); |
|
} |
|
if(fScannerState == SCANNER_STATE_CHARACTER_DATA){ |
|
bufferContent(); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private void bufferContent() { |
|
fContentBuffer.append(fTempString); |
|
|
|
fTempString.length = 0; |
|
fUsebuffer = true; |
|
} |
|
} // class XMLDocumentFragmentScannerImpl |