org.hibernate.boot.jaxb.internal.stax.JpaOrmXmlEventReader Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of hibernate-core Show documentation
Show all versions of hibernate-core Show documentation
Hibernate's core ORM functionality
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or .
*/
package org.hibernate.boot.jaxb.internal.stax;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import javax.xml.namespace.QName;
import javax.xml.stream.XMLEventFactory;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.events.Attribute;
import javax.xml.stream.events.EndElement;
import javax.xml.stream.events.Namespace;
import javax.xml.stream.events.StartElement;
import javax.xml.stream.events.XMLEvent;
import javax.xml.stream.util.EventReaderDelegate;
import org.hibernate.boot.xsd.LocalXsdResolver;
import org.hibernate.boot.xsd.MappingXsdSupport;
/**
* A JPA {@code orm.xml} specific StAX EVentReader to handle a few oddities.
*
* Mainly we handle the namespace change.
*
* Ultimately we should handle "upgrading" the documents as well. The idea being that
* we'd always treat all versions as the latest.
*
* {@see HHH-8108} for more discussion.
*
* @author Strong Liu
* @author Steve Ebersole
* @author Hardy Ferentschik
*/
public class JpaOrmXmlEventReader extends EventReaderDelegate {
private static final List NAMESPACE_URIS_TO_MAP = Arrays.asList(
// JPA 1.0 and 2.0 namespace uri
"http://java.sun.com/xml/ns/persistence/orm"
);
private static final String ROOT_ELEMENT_NAME = "entity-mappings";
private static final String VERSION_ATTRIBUTE_NAME = "version";
private final XMLEventFactory xmlEventFactory;
public JpaOrmXmlEventReader(XMLEventReader reader) {
this( reader, XMLEventFactory.newInstance() );
}
public JpaOrmXmlEventReader(XMLEventReader reader, XMLEventFactory xmlEventFactory) {
super( reader );
this.xmlEventFactory = xmlEventFactory;
}
@Override
public XMLEvent peek() throws XMLStreamException {
return wrap( super.peek() );
}
@Override
public XMLEvent nextEvent() throws XMLStreamException {
return wrap( super.nextEvent() );
}
private XMLEvent wrap(XMLEvent event) {
if ( event != null ) {
if ( event.isStartElement() ) {
return wrap( event.asStartElement() );
}
else if ( event.isEndElement() ) {
return wrap( event.asEndElement() );
}
}
return event;
}
private StartElement wrap(StartElement startElement) {
List newElementAttributeList = mapAttributes( startElement );
List newNamespaceList = mapNamespaces( startElement );
// Transfer the location info from the incoming event to the event factory
// so that the event we ask it to generate for us has the same location info
xmlEventFactory.setLocation( startElement.getLocation() );
return xmlEventFactory.createStartElement(
new QName( MappingXsdSupport.INSTANCE.latestJpaDescriptor().getNamespaceUri(), startElement.getName().getLocalPart() ),
newElementAttributeList.iterator(),
newNamespaceList.iterator()
);
}
private List mapAttributes(StartElement startElement) {
final List mappedAttributes = new ArrayList<>();
Iterator existingAttributesIterator = existingXmlAttributesIterator( startElement );
while ( existingAttributesIterator.hasNext() ) {
final Attribute originalAttribute = existingAttributesIterator.next();
final Attribute attributeToUse = mapAttribute( startElement, originalAttribute );
mappedAttributes.add( attributeToUse );
}
return mappedAttributes;
}
@SuppressWarnings("unchecked")
private Iterator existingXmlAttributesIterator(StartElement startElement) {
return startElement.getAttributes();
}
private Attribute mapAttribute(StartElement startElement, Attribute originalAttribute) {
// Here we look to see if this attribute is the JPA version attribute, and if so do 2 things:
// 1) validate its version attribute is valid
// 2) update its version attribute to the default version if not already
//
// NOTE : atm this is a very simple check using just the attribute's local name
// rather than checking its qualified name. It is possibly (though unlikely)
// that this could match on "other" version attributes in the same element
if ( ROOT_ELEMENT_NAME.equals( startElement.getName().getLocalPart() ) ) {
if ( VERSION_ATTRIBUTE_NAME.equals( originalAttribute.getName().getLocalPart() ) ) {
final String specifiedVersion = originalAttribute.getValue();
if ( !LocalXsdResolver.isValidJpaVersion( specifiedVersion ) ) {
throw new BadVersionException( specifiedVersion );
}
return xmlEventFactory.createAttribute( VERSION_ATTRIBUTE_NAME, LocalXsdResolver.latestJpaVerison() );
}
}
return originalAttribute;
}
private List mapNamespaces(StartElement startElement) {
return mapNamespaces( existingXmlNamespacesIterator( startElement ) );
}
private List mapNamespaces(Iterator originalNamespaceIterator ) {
final List mappedNamespaces = new ArrayList();
// final String elementNamespacePrefix = startElement.getName().getPrefix();
// if ( EMPTY_NAMESPACE_PREFIX.equals( elementNamespacePrefix ) ) {
// // add the default namespace mapping
// mappedNamespaces.add( xmlEventFactory.createNamespace( LocalSchema.ORM.getNamespaceUri() ) );
// }
while ( originalNamespaceIterator.hasNext() ) {
final Namespace originalNamespace = originalNamespaceIterator.next();
final Namespace mappedNamespace = mapNamespace( originalNamespace );
mappedNamespaces.add( mappedNamespace );
}
if ( mappedNamespaces.isEmpty() ) {
mappedNamespaces.add( xmlEventFactory.createNamespace( MappingXsdSupport.INSTANCE.latestJpaDescriptor().getNamespaceUri() ) );
}
return mappedNamespaces;
}
@SuppressWarnings("unchecked")
private Iterator existingXmlNamespacesIterator(StartElement startElement) {
return startElement.getNamespaces();
}
private Namespace mapNamespace(Namespace originalNamespace) {
if ( NAMESPACE_URIS_TO_MAP.contains( originalNamespace.getNamespaceURI() ) ) {
// this is a namespace "to map" so map it
return xmlEventFactory.createNamespace( originalNamespace.getPrefix(), MappingXsdSupport.INSTANCE.latestJpaDescriptor().getNamespaceUri() );
}
return originalNamespace;
}
private XMLEvent wrap(EndElement endElement) {
final List targetNamespaces = mapNamespaces( existingXmlNamespacesIterator( endElement ) );
// Transfer the location info from the incoming event to the event factory
// so that the event we ask it to generate for us has the same location info
xmlEventFactory.setLocation( endElement.getLocation() );
return xmlEventFactory.createEndElement(
new QName( MappingXsdSupport.INSTANCE.latestJpaDescriptor().getNamespaceUri(), endElement.getName().getLocalPart() ),
targetNamespaces.iterator()
);
}
@SuppressWarnings("unchecked")
private Iterator existingXmlNamespacesIterator(EndElement endElement) {
return endElement.getNamespaces();
}
public static class BadVersionException extends RuntimeException {
private final String requestedVersion;
public BadVersionException(String requestedVersion) {
this.requestedVersion = requestedVersion;
}
public String getRequestedVersion() {
return requestedVersion;
}
}
}