/*******************************************************************************
* Copyright (c) 1998, 2009 Oracle. 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.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.Map;
import java.util.Stack;
import javax.xml.namespace.QName;
import org.eclipse.persistence.exceptions.DescriptorException;
import org.eclipse.persistence.exceptions.EclipseLinkException;
import org.eclipse.persistence.exceptions.XMLMarshalException;
import org.eclipse.persistence.internal.oxm.XPathFragment;
import org.eclipse.persistence.internal.security.PrivilegedNewInstanceFromClass;
import org.eclipse.persistence.internal.sessions.AbstractSession;
import org.eclipse.persistence.oxm.XMLConstants;
import org.eclipse.persistence.oxm.XMLContext;
import org.eclipse.persistence.oxm.XMLDescriptor;
import org.eclipse.persistence.oxm.XMLUnmarshaller;
import org.eclipse.persistence.oxm.record.UnmarshalRecord;
import org.eclipse.persistence.oxm.unmapped.UnmappedContentHandler;
import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import org.xml.sax.SAXNotRecognizedException;
import org.xml.sax.SAXNotSupportedException;
import org.xml.sax.SAXParseException;
import org.xml.sax.ext.Locator2;
import org.eclipse.persistence.internal.oxm.record.XMLReader;
/**
* INTERNAL:
* <p><b>Purpose:</b>An implementation of ContentHandler used to handle the root element of an
* XML Document during unmarshal.
* <p><b>Responsibilities:</b><ul>
* <li>Implement ContentHandler interface</li>
* <li>Handle startElement event for the root-level element of an xml document</li>
* <li>Handle inheritance, and descriptor lookup to determine the correct class associated with
* this XML Element.</li>
* </ul>
*
* @author bdoughan
*
*/
public class SAXUnmarshallerHandler implements ContentHandler {
private static final String EMPTY_STRING = "";
private XMLReader xmlReader;
private XMLContext xmlContext;
private Object object;
private Map namespaceMap;
private XMLUnmarshaller unmarshaller;
private Map uriToPrefixMap;
private AbstractSession session;
private Locator2 locator;
public SAXUnmarshallerHandler(XMLContext xmlContext) {
super();
this.xmlContext = xmlContext;
}
public XMLReader getXMLReader() {
return this.xmlReader;
}
public void setXMLReader(XMLReader xmlReader) {
this.xmlReader = xmlReader;
}
public Object getObject() {
return this.object;
}
public void setObject(Object object) {
this.object = object;
}
public void setDocumentLocator(Locator locator) {
if (locator instanceof Locator2) {
this.locator = (Locator2)locator;
}
}
public void startDocument() throws SAXException {
}
public void endDocument() throws SAXException {
}
public void startPrefixMapping(String prefix, String uri) throws SAXException {
if (null == namespaceMap) {
namespaceMap = new HashMap();
}
if (uriToPrefixMap == null) {
uriToPrefixMap = new HashMap();
}
Stack uriStack = (Stack)namespaceMap.get(prefix);
if(uriStack == null) {
uriStack = new Stack();
namespaceMap.put(prefix, uriStack);
}
uriStack.push(uri);
Stack prefixStack = (Stack)uriToPrefixMap.get(uri);
if(prefixStack == null) {
prefixStack = new Stack();
uriToPrefixMap.put(uri, prefixStack);
}
prefixStack.push(prefix);
}
public void endPrefixMapping(String prefix) throws SAXException {
if (null == namespaceMap) {
return;
}
Stack uriStack = (Stack)namespaceMap.get(prefix);
String uri = null;
if(uriStack.size() > 0) {
uri = (String)uriStack.pop();
}
if(uri != null && uriToPrefixMap != null) {
Stack prefixStack = (Stack)uriToPrefixMap.get(uri);
if(prefixStack != null && prefixStack.size() > 0) {
prefixStack.pop();
}
}
}
/**
* INTERNAL:
*
* Resolve any mapping references.
*/
public void resolveReferences() {
unmarshaller.resolveReferences(session);
}
public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException {
try {
String name;
if (EMPTY_STRING.equals(localName) || (localName == null)) {
name = qName;
} else {
name = localName;
}
QName rootQName;
if (EMPTY_STRING.equals(namespaceURI) || (namespaceURI == null)) {
rootQName = new QName(name);
} else {
rootQName = new QName(namespaceURI, name);
}
XMLDescriptor xmlDescriptor = xmlContext.getDescriptor(rootQName);
// if there is no descriptor for the root element, we may be able to
// locate one if an xsi:type attribute is set:
if (null == xmlDescriptor) {
// Try to find a descriptor based on the schema type
String type = atts.getValue(XMLConstants.SCHEMA_INSTANCE_URL, "type");
if (null != type) {
XPathFragment typeFragment = new XPathFragment(type);
// set the prefix using a reverse key lookup by uri value on namespaceMap
if (null != namespaceMap) {
Stack namespaceStack = null;
if (null == typeFragment.getPrefix()) {
// an empty_string key references the default namespace
namespaceStack = (Stack)namespaceMap.get(EMPTY_STRING);
} else {
namespaceStack = (Stack)namespaceMap.get(typeFragment.getPrefix());
}
if(namespaceStack != null && namespaceStack.size() > 0) {
typeFragment.setNamespaceURI((String)namespaceStack.peek());
}
}
xmlDescriptor = xmlContext.getDescriptorByGlobalType(typeFragment);
}
if (null == xmlDescriptor) {
//check for a cached object and look for descriptor by class
Object obj = this.xmlReader.getCurrentObject(session, null);
if (obj != null) {
xmlDescriptor = (XMLDescriptor)xmlContext.getSession(obj.getClass()).getDescriptor(obj.getClass());
}
}
if (null == xmlDescriptor) {
Class unmappedContentHandlerClass = unmarshaller.getUnmappedContentHandlerClass();
if (null == unmappedContentHandlerClass) {
throw XMLMarshalException.noDescriptorWithMatchingRootElement(rootQName.toString());
} else {
UnmappedContentHandler unmappedContentHandler;
try {
PrivilegedNewInstanceFromClass privilegedNewInstanceFromClass = new PrivilegedNewInstanceFromClass(unmappedContentHandlerClass);
unmappedContentHandler = (UnmappedContentHandler)privilegedNewInstanceFromClass.run();
} catch (ClassCastException e) {
throw XMLMarshalException.unmappedContentHandlerDoesntImplement(e, unmappedContentHandlerClass.getName());
} catch (IllegalAccessException e) {
throw XMLMarshalException.errorInstantiatingUnmappedContentHandler(e, unmappedContentHandlerClass.getName());
} catch (InstantiationException e) {
throw XMLMarshalException.errorInstantiatingUnmappedContentHandler(e, unmappedContentHandlerClass.getName());
}
UnmappedContentHandlerWrapper unmappedContentHandlerWrapper = new UnmappedContentHandlerWrapper(unmappedContentHandler, this);
unmappedContentHandler.setUnmarshalRecord(unmappedContentHandlerWrapper);
unmappedContentHandler.startElement(namespaceURI, localName, qName, atts);
xmlReader.setContentHandler(unmappedContentHandler);
setObject(unmappedContentHandlerWrapper.getCurrentObject());
return;
}
}
}
// for XMLObjectReferenceMappings we need a non-shared cache, so
// try and get a Unit Of Work from the XMLContext
session = xmlContext.getReadSession(xmlDescriptor);
UnmarshalRecord unmarshalRecord;
if (xmlDescriptor.hasInheritance()) {
unmarshalRecord = new UnmarshalRecord(null);
unmarshalRecord.setNamespaceMap(namespaceMap);
unmarshalRecord.setUriToPrefixMap(uriToPrefixMap);
unmarshalRecord.setAttributes(atts);
Class classValue = xmlDescriptor.getInheritancePolicy().classFromRow(unmarshalRecord, session);
if (classValue == null) {
// no xsi:type attribute - look for type indicator on the default root element
QName leafElementType = xmlDescriptor.getDefaultRootElementType();
// if we have a user-set type, try to get the class from the inheritance policy
if (leafElementType != null) {
Object indicator = xmlDescriptor.getInheritancePolicy().getClassIndicatorMapping().get(leafElementType);
// if the inheritance policy does not contain the user-set type, throw an exception
if (indicator == null) {
throw DescriptorException.missingClassForIndicatorFieldValue(leafElementType, xmlDescriptor.getInheritancePolicy().getDescriptor());
}
classValue = (Class)indicator;
}
}
if (classValue != null) {
xmlDescriptor = (XMLDescriptor)session.getDescriptor(classValue);
} else {
// since there is no xsi:type attribute, we'll use the descriptor
// that was retrieved based on the rootQName - we need to make
// sure it is non-abstract
if (Modifier.isAbstract(xmlDescriptor.getJavaClass().getModifiers())) {
// need to throw an exception here
throw DescriptorException.missingClassIndicatorField(unmarshalRecord, xmlDescriptor.getInheritancePolicy().getDescriptor());
}
}
}
unmarshalRecord = (UnmarshalRecord)xmlDescriptor.getObjectBuilder().createRecord(session);
if (locator != null) {
unmarshalRecord.setDocumentLocator(locator);
}
unmarshalRecord.setUnmarshaller(this.unmarshaller);
unmarshalRecord.setXMLReader(this.getXMLReader());
unmarshalRecord.startDocument();
unmarshalRecord.setNamespaceMap(namespaceMap);
unmarshalRecord.setUriToPrefixMap(uriToPrefixMap);
unmarshalRecord.startElement(namespaceURI, localName, qName, atts);
xmlReader.setContentHandler(unmarshalRecord);
try {
unmarshalRecord.getXMLReader().setProperty("http://xml.org/sax/properties/lexical-handler", unmarshalRecord);
} catch (SAXNotRecognizedException ex) {
} catch (SAXNotSupportedException ex) {
//if lexical handling is not supported by this parser, just ignore.
}
// if we located the descriptor via xsi:type attribute, create and
// return an XMLRoot object
object = xmlDescriptor.wrapObjectInXMLRoot(unmarshalRecord, unmarshaller.isResultAlwaysXMLRoot());
} catch (EclipseLinkException e) {
if (null == xmlReader.getErrorHandler()) {
throw e;
} else {
SAXParseException saxParseException = new SAXParseException(null, null, null, 0, 0, e);
xmlReader.getErrorHandler().error(saxParseException);
}
}
}
public void endElement(String namespaceURI, String localName, String qName) throws SAXException {
}
public void characters(char[] ch, int start, int length) throws SAXException {
}
public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException {
}
public void processingInstruction(String target, String data) throws SAXException {
}
public void skippedEntity(String name) throws SAXException {
}
public void setUnmarshaller(XMLUnmarshaller unmarshaller) {
this.unmarshaller = unmarshaller;
}
public XMLUnmarshaller getUnmarshaller() {
return this.unmarshaller;
}
public Map getUriToPrefixMap() {
return this.uriToPrefixMap;
}
public void setUriToPrefixMap(Map uriToPrefixMap) {
this.uriToPrefixMap = uriToPrefixMap;
}
public Map getNamespaceMap() {
return this.namespaceMap;
}
}