blob: 82ff85cd3b5eaf39a72326ee7fee403d11b9b4a2 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 1998, 2013 Oracle and/or its affiliates. All rights reserved.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0
* which accompanies this distribution.
* The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Oracle - initial API and implementation from Oracle TopLink
******************************************************************************/
package org.eclipse.persistence.internal.oxm.record;
import java.io.IOException;
import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.DTDHandler;
import org.xml.sax.EntityResolver;
import org.xml.sax.ErrorHandler;
import org.xml.sax.InputSource;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import org.xml.sax.SAXNotRecognizedException;
import org.xml.sax.SAXNotSupportedException;
import org.xml.sax.ext.LexicalHandler;
import javax.xml.namespace.QName;
import javax.xml.validation.ValidatorHandler;
import org.eclipse.persistence.internal.core.sessions.CoreAbstractSession;
import org.eclipse.persistence.internal.oxm.Constants;
import org.eclipse.persistence.internal.oxm.MediaType;
import org.eclipse.persistence.internal.oxm.XMLConversionManager;
import org.eclipse.persistence.internal.oxm.mappings.Field;
import org.eclipse.persistence.internal.oxm.mappings.Mapping;
import org.eclipse.persistence.oxm.mappings.nullpolicy.AbstractNullPolicy;
/**
* INTERNAL:
* <p><b>Purpose:</b>Provide a wrapper for an org.xml.sax.XMLReader instance and define some extra
* event methods that can be used by TopLink during the unmarshal process. These events are no ops
* in this class, but may be overridden in subclasses.
* <p><b>Responsibilities</b><ul>
* <li>Wrap an instance of org.xml.sax.XMLReader and provide all the required API</li>
* <li>Provide empty implementations of some callback methods that can be overridden in subclasses</li>
*
* @see org.eclipse.persistence.internal.oxm.record.DOMReader
* @author mmacivor
* @since release specific (what release of product did this appear in)
*/
public class XMLReader implements org.xml.sax.XMLReader {
public static final String NAMESPACE_PREFIXES_FEATURE = "http://xml.org/sax/features/namespace-prefixes";
public static final String REPORT_IGNORED_ELEMENT_CONTENT_WHITESPACE_FEATURE = "http://java.sun.com/xml/schema/features/report-ignored-element-content-whitespace";
private org.xml.sax.XMLReader reader;
private boolean supportsLexicalHandler;
private LexicalHandlerWrapper lexicalHandlerWrapper;
protected ValidatingContentHandler validatingContentHandler;
protected boolean namespaceAware;
protected char namespaceSeparator;
protected Locator locator;
public XMLReader(org.xml.sax.XMLReader internalReader) {
this();
this.reader = internalReader;
}
public XMLReader() {
this.supportsLexicalHandler = true;
namespaceAware = true;
namespaceSeparator = Constants.COLON;
}
/**
* INTERNAL:
* return the Locator object associated with this reader
* @since 2.4
*/
public Locator getLocator(){
return locator;
}
/**
* INTERNAL:
* set the Locator object to associate with this reader
* @since 2.4
*/
public void setLocator(Locator newLocator){
locator = newLocator;
}
public ContentHandler getContentHandler () {
return reader.getContentHandler();
}
public void setContentHandler (ContentHandler handler) {
if(validatingContentHandler != null) {
validatingContentHandler.setContentHandler(handler);
} else {
reader.setContentHandler(handler);
}
}
/**
* INTERNAL:
* Determine if namespaces will be considered during marshal/unmarshal operations.
* @since 2.4
*/
public boolean isNamespaceAware() {
return namespaceAware;
}
/**
* INTERNAL:
* The character used to separate the prefix and uri portions when namespaces are present
* @since 2.4
*/
public char getNamespaceSeparator(){
return namespaceSeparator;
}
/**
* INTERNAL:
* @return The MediaType associated with this reader
*/
public MediaType getMediaType(){
return Constants.APPLICATION_XML;
}
/**
* INTERNAL:
* @since 2.4
*/
public Object convertValueBasedOnSchemaType(Field xmlField, Object value, XMLConversionManager xmlConversionManager, AbstractUnmarshalRecord record) {
return xmlField.convertValueBasedOnSchemaType(value, xmlConversionManager, record);
}
public DTDHandler getDTDHandler () {
return reader.getDTDHandler();
}
public void setDTDHandler (DTDHandler handler) {
reader.setDTDHandler(handler);
}
public void setEntityResolver (EntityResolver resolver) {
reader.setEntityResolver(resolver);
}
public EntityResolver getEntityResolver () {
return reader.getEntityResolver();
}
public ErrorHandler getErrorHandler () {
return reader.getErrorHandler();
}
public void setErrorHandler (ErrorHandler handler) {
if(validatingContentHandler != null) {
validatingContentHandler.setErrorHandler(handler);
} else {
reader.setErrorHandler(handler);
}
}
public LexicalHandler getLexicalHandler() {
if(supportsLexicalHandler) {
try {
return (LexicalHandler) reader.getProperty(Constants.LEXICAL_HANDLER_PROPERTY);
} catch (SAXException e) {
supportsLexicalHandler = false;
}
}
return null;
}
public void setLexicalHandler(LexicalHandler lexicalHandler) {
if(supportsLexicalHandler) {
if(null == lexicalHandlerWrapper) {
try {
lexicalHandlerWrapper = new LexicalHandlerWrapper(lexicalHandler);
reader.setProperty(Constants.LEXICAL_HANDLER_PROPERTY, lexicalHandlerWrapper);
} catch (SAXException e) {
supportsLexicalHandler = false;
}
} else {
lexicalHandlerWrapper.setLexicalHandler(lexicalHandler);
}
}
}
public boolean getFeature (String name) throws SAXNotRecognizedException, SAXNotSupportedException {
return reader.getFeature(name);
}
public void setFeature (String name, boolean value) throws SAXNotRecognizedException, SAXNotSupportedException {
reader.setFeature(name, value);
}
public Object getProperty (String name) throws SAXNotRecognizedException, SAXNotSupportedException {
if(Constants.LEXICAL_HANDLER_PROPERTY.equals(name)) {
return getLexicalHandler();
} else {
return reader.getProperty(name);
}
}
public void setProperty (String name, Object value) throws SAXNotRecognizedException, SAXNotSupportedException {
if(Constants.LEXICAL_HANDLER_PROPERTY.equals(name)) {
setLexicalHandler((LexicalHandler) value);
} else {
reader.setProperty(name, value);
}
}
public void parse(InputSource input) throws IOException, SAXException {
try {
reader.parse(input);
} catch(SAXNotSupportedException e) {
String message = e.getMessage();
if(message != null && message.contains("namespace-prefix")) {
reader.setFeature(NAMESPACE_PREFIXES_FEATURE, false);
reader.parse(input);
} else {
throw e;
}
}
}
public void parse (String systemId) throws IOException, SAXException {
try {
reader.parse(systemId);
} catch(SAXNotSupportedException e) {
String message = e.getMessage();
if(message != null && message.contains("namespace-prefix")) {
reader.setFeature(NAMESPACE_PREFIXES_FEATURE, false);
reader.parse(systemId);
} else {
throw e;
}
}
}
public void setValidatorHandler(ValidatorHandler validatorHandler) {
ErrorHandler errorHandler = getErrorHandler();
ContentHandler contentHandler;
if(null == this.validatingContentHandler) {
contentHandler = getContentHandler();
} else {
contentHandler = validatorHandler.getContentHandler();
this.validatingContentHandler = null;
}
ValidatingContentHandler validatingContentHandler = null;
if(null != validatorHandler) {
validatingContentHandler = new ValidatingContentHandler(validatorHandler);
validatingContentHandler.setContentHandler(contentHandler);
contentHandler = validatingContentHandler;
}
if(null != reader) {
reader.setContentHandler(contentHandler);
}
setContentHandler(contentHandler);
this.validatingContentHandler = validatingContentHandler;
setErrorHandler(errorHandler);
}
public ValidatorHandler getValidatorHandler() {
if(null == validatingContentHandler) {
return null;
}
return this.validatingContentHandler.getValidatorHandler();
}
public void newObjectEvent(Object object, Object parent, Mapping selfRecordMapping) {
//no op in this class.
}
public Object getCurrentObject(CoreAbstractSession session, Mapping selfRecordMapping) {
return null;
}
/**
* This call back mechanism provides an opportunity for the XMLReader to
* provide an alternate conversion. This optimization is currently only
* leveraged for properties annotated with @XmlInlineBinaryData.
* @param characters The characters to be converted.
* @param dataType The type to be converted to.
* @return The converted value
*/
public Object getValue(CharSequence characters, Class<?> dataType) {
return null;
}
public boolean isNullRepresentedByXsiNil(AbstractNullPolicy nullPolicy){
return nullPolicy.isNullRepresentedByXsiNil();
}
public boolean isNullRecord(AbstractNullPolicy nullPolicy, Attributes atts, UnmarshalRecord record) {
boolean isNil = isNullRepresentedByXsiNil(nullPolicy) && record.isNil();
if (!nullPolicy.ignoreAttributesForNil()) {
return isNil && !hasAttributes(atts);
}
return isNil;
}
private boolean hasAttributes(Attributes attributes) {
QName nilAttrName = new QName(javax.xml.XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI, Constants.SCHEMA_NIL_ATTRIBUTE);
for (int i = 0; i < attributes.getLength(); i++) {
if (!(nilAttrName.getNamespaceURI().equals(attributes.getURI(i)) &&
nilAttrName.getLocalPart().equals(attributes.getLocalName(i)))) {
return true;
}
}
return false;
}
public boolean isInCollection(){
return true;
}
/**
* Performance Optimization:
* It is expensive to change the LexicalHandler on the underlying XMLReader
* constantly through the setProperty(String, Object) mechanism. So instead
* the LexicalHandlerWrapper is set once this way, and the "real"
* LexicalHandler is changed on the LexicalHandlerWrapper.
*/
private static class LexicalHandlerWrapper implements LexicalHandler {
private LexicalHandler lexicalHandler;
public LexicalHandlerWrapper(LexicalHandler lexicalHandler) {
this.lexicalHandler = lexicalHandler;
}
public void setLexicalHandler(LexicalHandler lexicalHandler) {
this.lexicalHandler = lexicalHandler;
}
public void comment(char[] ch, int start, int length) throws SAXException {
if(null != lexicalHandler) {
lexicalHandler.comment(ch, start, length);
}
}
public void endCDATA() throws SAXException {
if(null != lexicalHandler) {
lexicalHandler.endCDATA();
}
}
public void endDTD() throws SAXException {
if(null != lexicalHandler) {
lexicalHandler.endDTD();
}
}
public void endEntity(String name) throws SAXException {
if(null != lexicalHandler) {
lexicalHandler.endEntity(name);
}
}
public void startCDATA() throws SAXException {
if(null != lexicalHandler) {
lexicalHandler.startCDATA();
}
}
public void startDTD(String name, String publicId, String systemId) throws SAXException {
if(null != lexicalHandler) {
lexicalHandler.startCDATA();
}
}
public void startEntity(String name) throws SAXException {
if(null != lexicalHandler) {
lexicalHandler.startEntity(name);
}
}
}
/**
* Validate the SAX events reported to the ContentHandler. This class is
* being used rather than a ValidatorHandler in order to prevent default
* values from being populated.
*/
protected static class ValidatingContentHandler implements ContentHandler {
private ValidatorHandler validatorHandler;
private ContentHandler contentHandler;
public ValidatingContentHandler(ValidatorHandler validatorHandler) {
this.validatorHandler = validatorHandler;
}
public ContentHandler getContentHandler() {
return contentHandler;
}
public void setContentHandler(ContentHandler contentHandler) {
this.contentHandler = contentHandler;
}
public void setErrorHandler(ErrorHandler errorHandler) {
validatorHandler.setErrorHandler(errorHandler);
}
public ValidatorHandler getValidatorHandler() {
return validatorHandler;
}
public void setValidatorHandler(ValidatorHandler validatorHandler) {
this.validatorHandler = validatorHandler;
}
public void setDocumentLocator(Locator locator) {
validatorHandler.setDocumentLocator(locator);
contentHandler.setDocumentLocator(locator);
}
public void startDocument() throws SAXException {
validatorHandler.startDocument();
contentHandler.startDocument();
}
public void endDocument() throws SAXException {
validatorHandler.endDocument();
contentHandler.endDocument();
}
public void startPrefixMapping(String prefix, String uri) throws SAXException {
validatorHandler.startPrefixMapping(prefix, uri);
contentHandler.startPrefixMapping(prefix, uri);
}
public void endPrefixMapping(String prefix) throws SAXException {
validatorHandler.endPrefixMapping(prefix);
contentHandler.endPrefixMapping(prefix);
}
public void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException {
validatorHandler.startElement(uri, localName, qName, atts);
contentHandler.startElement(uri, localName, qName, atts);
}
public void endElement(String uri, String localName, String qName) throws SAXException {
validatorHandler.endElement(uri, localName, qName);
contentHandler.endElement(uri, localName, qName);
}
public void characters(char[] ch, int start, int length) throws SAXException {
validatorHandler.characters(ch, start, length);
contentHandler.characters(ch, start, length);
}
public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException {
validatorHandler.ignorableWhitespace(ch, start, length);
contentHandler.characters(ch, start, length);
}
public void processingInstruction(String target, String data) throws SAXException {
validatorHandler.processingInstruction(target, data);
contentHandler.processingInstruction(target, data);
}
public void skippedEntity(String name) throws SAXException {
validatorHandler.skippedEntity(name);
contentHandler.skippedEntity(name);
}
}
}