| /* |
| * Copyright (c) 1998, 2020 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 v. 2.0 which is available at |
| * http://www.eclipse.org/legal/epl-2.0, |
| * or the Eclipse Distribution License v. 1.0 which is available at |
| * http://www.eclipse.org/org/documents/edl-v10.php. |
| * |
| * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause |
| */ |
| |
| // Contributors: |
| // bdoughan - Jan 27/2009 - 1.1 - Initial implementation |
| package org.eclipse.persistence.sdo.helper.jaxb; |
| |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.List; |
| import java.util.Map; |
| |
| import jakarta.xml.bind.JAXBContext; |
| import javax.xml.namespace.QName; |
| |
| import org.eclipse.persistence.exceptions.SDOException; |
| import org.eclipse.persistence.internal.helper.IdentityWeakHashMap; |
| import org.eclipse.persistence.internal.oxm.XPathFragment; |
| import org.eclipse.persistence.oxm.XMLDescriptor; |
| import org.eclipse.persistence.oxm.schema.XMLSchemaReference; |
| import org.eclipse.persistence.sdo.SDODataObject; |
| import org.eclipse.persistence.sdo.SDOType; |
| import org.eclipse.persistence.sdo.helper.SDOCopyHelper; |
| import org.eclipse.persistence.sdo.helper.SDODataHelper; |
| import org.eclipse.persistence.sdo.helper.SDOEqualityHelper; |
| import org.eclipse.persistence.sdo.helper.SDOHelperContext; |
| import org.eclipse.persistence.sdo.helper.delegates.SDOTypeHelperDelegate; |
| import org.eclipse.persistence.sdo.helper.delegates.SDOXSDHelperDelegate; |
| |
| import commonj.sdo.DataObject; |
| import commonj.sdo.Property; |
| import commonj.sdo.Type; |
| |
| /** |
| * <p>The JAXBHelperContext is a bridge between POJOs and SDO DataObjects. The bridge |
| * is based on their corresponding XML representations. For the POJOs the XML |
| * representation is specified using JAXB annotations or object-to-XML mappings.</p> |
| * |
| * <p>The following steps are required to create the JAXBHelperContext. The XML schema |
| * used in step #3 is the same one that the POJOs are mapped to. This step has been |
| * separated so that SDO annotations could be added to the XML schema. |
| * <p>Step #1 - Create the JAXBContext</p> |
| * <pre> |
| * JAXBContext jaxbContext = JAXBContext.newInstance("com.example.customer"); |
| * </pre> |
| * <p>Step #2 - Create the JAXBHelperContext</p> |
| * <pre> |
| * JAXBHelperContext jaxbHelperContext = new JAXBHelperContext(jaxbContext); |
| * </pre> |
| * <p>Step #3 - Create the SDO Metadata from an XML Schema</p> |
| * <pre> |
| * jaxbHelperContext.getXSDHelper().define(xmlSchema); |
| * </pre> |
| * |
| * <p> |
| * The JAXBHelperContext allows you to convert between POJOs and DataObjects using |
| * a wrap operation. |
| * <pre> |
| * Customer customer = new Customer(); |
| * Address address new Address(); |
| * address.setStreet("123 Any Street"); |
| * customer.set(address); |
| * |
| * DataObject customerDO = jaxbHelperContext.wrap(customer); |
| * customerDO.getString("address/street"); // returns "123 Any Street" |
| * </pre> |
| * |
| * <p> |
| * The JAXBHelperContext allows you to convert between DataObjects and POJOs using |
| * an unwrap operation. |
| * <pre> |
| * Type customerType = jaxbHelperContext.getType(Customer.class); |
| * DataObject customerDO = jaxbHelperContext.getDataFactory().create(customerType); |
| * customerDO.set("first-name", "Jane"); |
| * |
| * Customer customer = jaxbHelperContext.unwrap(customerDO); |
| * customer.getFirstName(); // returns "Jane" |
| * </pre> |
| * |
| * <p> |
| * Of course the POJOs may be JPA entities. Below is an example of wrapping the |
| * results of a JPA query. |
| * <pre> |
| * EntityManagerFactory emf = Persistence.createEntityManagerFactory("CustomerExample"); |
| * EntityManager em = emf.createEntityManager(); |
| * {@literal List<MyEntity>} entities = em.createQuery("SELECT e FROM MyEntity e WHERE ...").getResultList(); |
| * {@literal List<DataObject>} dataObjects = hc.wrap(entities); |
| * </pre> |
| */ |
| public class JAXBHelperContext extends SDOHelperContext { |
| |
| private org.eclipse.persistence.jaxb.JAXBContext jaxbContext; |
| private Map<Object, SDODataObject> wrapperDataObjects; |
| |
| /** |
| * Create a new instance of JAXBHelperContext |
| * @param aJAXBContext - The JAXBContext representing the class to XML schema mapping. |
| */ |
| public JAXBHelperContext(JAXBContext aJAXBContext) { |
| this(aJAXBContext, Thread.currentThread().getContextClassLoader()); |
| } |
| |
| /** |
| * Create a new instance of JAXBHelperContext |
| * @param aJAXBContext - The JAXBContext representing the class to XML schema mapping. |
| * @param aClassLoader - The ClassLoader containing the generated SDO classes/interfaces (if any). |
| */ |
| public JAXBHelperContext(JAXBContext aJAXBContext, ClassLoader aClassLoader) { |
| super(aClassLoader); |
| wrapperDataObjects = new IdentityWeakHashMap<Object, SDODataObject>(); |
| jaxbContext = (org.eclipse.persistence.jaxb.JAXBContext) aJAXBContext; |
| } |
| |
| /** |
| * Override the default helpers/factories, replacing them |
| * with JAXB aware ones where necessary. |
| */ |
| @Override |
| protected void initialize(ClassLoader aClassLoader) { |
| copyHelper = new SDOCopyHelper(this); |
| dataFactory = new JAXBDataFactory(this); |
| dataHelper = new SDODataHelper(this); |
| equalityHelper = new SDOEqualityHelper(this); |
| xmlHelper = new JAXBXMLHelper(this, aClassLoader); |
| typeHelper = new SDOTypeHelperDelegate(this); |
| xsdHelper = new SDOXSDHelperDelegate(this); |
| } |
| |
| /** |
| * Return the JAXBContext. The JAXBContext represents the Java |
| * class to XML schema information for the POJOs. |
| */ |
| public JAXBContext getJAXBContext() { |
| return jaxbContext; |
| } |
| |
| /** |
| * Return the SDO type corresponding to the wrapped class. |
| * <pre> |
| * Type customerType = jaxbHelperContext.getType(Customer.class); |
| * DataObject customerDO = jaxbHelperContext.getDataFactory().create(customerType); |
| * </pre> |
| */ |
| public Type getType(Class entityClass) { |
| if(null == entityClass) { |
| return null; |
| } |
| |
| XMLDescriptor entityDescriptor = null; |
| try { |
| entityDescriptor = (XMLDescriptor) jaxbContext.getXMLContext().getSession(entityClass).getDescriptor(entityClass); |
| } catch(Exception e) { |
| return null; |
| } |
| |
| XMLSchemaReference schemaReference = entityDescriptor.getSchemaReference(); |
| if(null == schemaReference) { |
| throw SDOException.sdoJaxbNoSchemaReference(entityClass); |
| } |
| |
| QName qName = schemaReference.getSchemaContextAsQName(entityDescriptor.getNamespaceResolver()); |
| if(null == qName) { |
| throw SDOException.sdoJaxbNoSchemaContext(entityClass); |
| } |
| |
| Type wrapperType; |
| if(entityDescriptor.getSchemaReference().getType() == XMLSchemaReference.COMPLEX_TYPE) { |
| wrapperType = getTypeHelper().getType(qName.getNamespaceURI(), qName.getLocalPart()); |
| } else { |
| Property property = getXSDHelper().getGlobalProperty(qName.getNamespaceURI(), qName.getLocalPart(), true); |
| wrapperType = property.getType(); |
| } |
| if(null == wrapperType) { |
| throw SDOException.sdoJaxbNoTypeForClassBySchemaContext(entityClass, qName); |
| } |
| return wrapperType; |
| } |
| |
| /** |
| * Return the wrapped class corresponding to the SDO type. |
| * <pre> |
| * Type customerType = jaxbHelperContext.getTypeHelper().getType("urn:customer", "customer"); |
| * Class customerClass = jaxbHelperContext.getClass(customerType); |
| * </pre> |
| */ |
| public Class getClass(Type type) { |
| if(null == type) { |
| return null; |
| } |
| return getObjectDescriptor((SDOType) type).getJavaClass(); |
| } |
| |
| /** |
| * Return a DataObject that wraps a POJO. This call should be |
| * made on the root POJO. |
| * <pre> |
| * Customer customer = new Customer(); |
| * Address address new Address(); |
| * address.setStreet("123 Any Street"); |
| * customer.set(address); |
| * |
| * DataObject customerDO = jaxbHelperContext.wrap(customer); |
| * customerDO.getString("address/street"); // returns "123 Any Street" |
| * </pre> |
| * Multiple calls to wrap for the same instance POJO return the |
| * same instance of DataObject, in other words the following is |
| * always true: |
| * <pre> |
| * jaxbHelperContext.wrap(customer123) == jaxbHelperContext.wrap(customer123) |
| * jaxbHelperContext.wrap(customer123) != jaxbHelperContext.wrap(customer456) |
| * </pre> |
| */ |
| public DataObject wrap(Object entity) { |
| if(null == entity) { |
| return null; |
| } |
| SDODataObject wrapperDO = wrapperDataObjects.get(entity); |
| if(null != wrapperDO) { |
| return wrapperDO; |
| } |
| |
| Type wrapperType = getType(entity.getClass()); |
| if(null == wrapperType) { |
| throw SDOException.sdoJaxbNoTypeForClass(entity.getClass()); |
| } |
| wrapperDO = (SDODataObject) getDataFactory().create(wrapperType); |
| |
| JAXBValueStore jaxbValueStore = new JAXBValueStore(this, entity); |
| wrapperDO._setCurrentValueStore(jaxbValueStore); |
| jaxbValueStore.initialize(wrapperDO); |
| |
| wrapperDataObjects.put(entity, wrapperDO); |
| return wrapperDO; |
| } |
| |
| /** |
| * Helper method that configures container information. |
| */ |
| DataObject wrap(Object entity, Property containmentProperty, DataObject container) { |
| SDODataObject sdoDataObject = (SDODataObject) wrap(entity); |
| if(null == container) { |
| sdoDataObject._setContainmentPropertyName(null); |
| } else { |
| sdoDataObject._setContainmentPropertyName(containmentProperty.getName()); |
| } |
| sdoDataObject._setContainer(container); |
| return sdoDataObject; |
| } |
| |
| /** |
| * Perform the wrap operation on each of the POJOs in the collection, |
| * and return the results as a List. |
| */ |
| public List<DataObject> wrap(Collection<Object> entities) { |
| return wrap(entities, null, null); |
| } |
| |
| /** |
| * Helper method that configures container information. |
| */ |
| List<DataObject> wrap(Collection<Object> entities, Property containmentProperty, DataObject container) { |
| if(null == entities) { |
| return new ArrayList<DataObject>(0); |
| } |
| List<DataObject> dataObjects = new ArrayList<DataObject>(entities.size()); |
| for(Object entity: entities) { |
| dataObjects.add(wrap(entity, containmentProperty, container)); |
| } |
| return dataObjects; |
| } |
| |
| /** |
| * Return the POJO that is wrapped by the DataObject. |
| * <pre> |
| * Type customerType = jaxbHelperContext.getType(Customer.class); |
| * DataObject customerDO = jaxbHelperContext.getDataFactory().create(customerType); |
| * DataObject addressDO = customerDO.create("address"); |
| * addressDO.set("street", "123 Any Street"); |
| * |
| * Customer customer = (Customer) jaxbHelperContext.unwrap(customerDO); |
| * customer.getAddress().getStreet(); // returns "123 Any Street" |
| * </pre> |
| * Multiple calls to unwrap for the same DataObject must return the |
| * same instance of Object, in other words the following is always true: |
| * <pre> |
| * jaxbHelperContext.unwrap(customerDO123) == jaxbHelperContext.unwrap(customerDO123) |
| * jaxbHelperContext.unwrap(customerDO123) != jaxbHelperContext.unwrap(customerDO456) |
| * customer123 == jaxbHelperContext.unwrap(jaxbHelperContext.wrap(customer123)) |
| * </pre> |
| */ |
| public Object unwrap(DataObject dataObject) { |
| try { |
| if(null == dataObject) { |
| return null; |
| } |
| SDODataObject sdoDataObject = (SDODataObject) dataObject; |
| JAXBValueStore jpaValueStore = (JAXBValueStore) sdoDataObject._getCurrentValueStore(); |
| return jpaValueStore.getEntity(); |
| } catch(ClassCastException e) { |
| return null; |
| } |
| } |
| |
| /** |
| * Perform the unwrap operation on each of the DataObjects in the collection, |
| * and return the results as a List. |
| */ |
| public List<Object> unwrap(Collection<DataObject> dataObjects) { |
| if(null == dataObjects) { |
| return new ArrayList<Object>(0); |
| } |
| List<Object> entities = new ArrayList<Object>(dataObjects.size()); |
| for(DataObject dataObject: dataObjects) { |
| entities.add(unwrap(dataObject)); |
| } |
| return entities; |
| } |
| |
| /** |
| * Maintain an association between this POJO and DataObject. |
| */ |
| void putWrapperDataObject(Object anObject, SDODataObject aDataObject) { |
| wrapperDataObjects.put(anObject, aDataObject); |
| } |
| |
| /** |
| * Get the XML descriptor for the entity class corresponding to the SDO type. |
| */ |
| XMLDescriptor getObjectDescriptor(SDOType sdoType) { |
| QName xsdQName = sdoType.getXsdType(); |
| if(null == xsdQName) { |
| xsdQName = sdoType.getQName(); |
| } |
| XPathFragment xPathFragment = new XPathFragment(xsdQName.getLocalPart()); |
| xPathFragment.setNamespaceURI(xsdQName.getNamespaceURI()); |
| XMLDescriptor xmlDescriptor = jaxbContext.getXMLContext().getDescriptorByGlobalType(xPathFragment); |
| if (null == xmlDescriptor) { |
| xmlDescriptor = jaxbContext.getXMLContext().getDescriptor(xsdQName); |
| if (null == xmlDescriptor) { |
| throw SDOException.sdoJaxbNoDescriptorForType(sdoType.getQName(), xsdQName); |
| } |
| } |
| return xmlDescriptor; |
| } |
| |
| } |