| /* |
| * Copyright (c) 1998, 2021 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: |
| // Oracle - initial API and implementation from Oracle TopLink |
| package org.eclipse.persistence.oxm; |
| |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.Map; |
| import java.util.StringTokenizer; |
| |
| import javax.xml.datatype.XMLGregorianCalendar; |
| import javax.xml.namespace.QName; |
| |
| import org.eclipse.persistence.exceptions.XMLMarshalException; |
| import org.eclipse.persistence.internal.core.helper.CoreClassConstants; |
| import org.eclipse.persistence.internal.core.sessions.CoreAbstractSession; |
| import org.eclipse.persistence.internal.helper.DatabaseField; |
| import org.eclipse.persistence.internal.helper.DatabaseTable; |
| import org.eclipse.persistence.internal.oxm.ConversionManager; |
| import org.eclipse.persistence.internal.oxm.XMLConversionManager; |
| import org.eclipse.persistence.internal.oxm.XMLConversionPair; |
| import org.eclipse.persistence.internal.oxm.XPathFragment; |
| import org.eclipse.persistence.internal.oxm.XPathPredicate; |
| import org.eclipse.persistence.internal.oxm.mappings.Field; |
| import org.eclipse.persistence.internal.oxm.record.AbstractUnmarshalRecord; |
| |
| /** |
| * TopLink XML mappings make use of XMLFields based on XPath statements to find the relevant |
| * data in an XML document. The XPath statement is relative to the context node specified in the descriptor. |
| * The XPath may contain node type, path, and positional information. The XPath is specified on the |
| * field using the <code>setXPath</code> method or by using the appropriate constructor. |
| * |
| * <p>The following XPath statements may be used to specify the location of XML data relating to an object's |
| * name attribute: |
| * |
| * <table border="1"> |
| * <caption>XPath statements</caption> |
| * <tr> |
| * <th id="c1">XPath</th> |
| * <th id="c2">Description</th> |
| * </tr> |
| * <tr> |
| * <td headers="c1">@name</td> |
| * <td headers="c2">The "@" character indicates that the node is an attribute.</td> |
| * </tr> |
| * <tr> |
| * <td headers="c1">text()</td> |
| * <td headers="c2">"text()" indicates that the node is a text node. In this case the name value in the |
| * text node belongs to the context node.</td> |
| * </tr> |
| * <tr> |
| * <td headers="c1">full-name/text()</td> |
| * <td headers="c2">The name information is stored in the text node of the full-name element.</td> |
| * </tr> |
| * <tr> |
| * <td headers="c1" style="nowrap">personal-info/name/text()</td> |
| * <td headers="c2">The XPath statement may be used to specify any valid path.</td> |
| * </tr> |
| * <tr> |
| * <td headers="c1">name[2]/text()</td> |
| * <td headers="c2">The XPath statement may contain positional information. In this case the name |
| * information is stored in the text node of the second occurrence of the name element.</td> |
| * </tr> |
| * </table> |
| * <p><b>Mapping to a Specific Schema Type</b>: In most cases TopLink can determine the target format in the |
| * XML document. However, there are cases where you must specify which one of a number of possible targets |
| * TopLink should use. For example, a java.util.Calendar could be marshalled to a schema date, time, or dateTime, |
| * or a byte[] could be marshalled to a schema hexBinary or base64Binary node. |
| * |
| * <!-- |
| * <?xml version="1.0" encoding="UTF-8"?> |
| * <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"> |
| * <xsd:element name="customer" type="customer-type"/> |
| * <xsd:complexType name="customer-type"> |
| * <xsd:sequence> |
| * <xsd:element name="picture" type="xsd:hexBinary"/> |
| * <xsd:element name="resume" type="xsd:base64Binary"/> |
| * </xsd:sequence> |
| * </xsd:complexType> |
| * </xsd:schema> |
| * --> |
| * |
| * <p><em>XML Schema</em><br> |
| * <code> |
| * <?xml version="1.0" encoding="UTF-8"?><br> |
| * <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"><br> |
| * <xsd:element name="customer" type="customer-type"/><br> |
| * <xsd:complexType name="customer-type"><br> |
| * <xsd:sequence><br> |
| * <xsd:element name="picture" type="xsd:hexBinary"/><br> |
| * <xsd:element name="resume" type="xsd:base64Binary"/><br> |
| * </xsd:sequence><br> |
| * </xsd:complexType><br> |
| * </xsd:schema><br> |
| * </code> |
| * |
| * <p><em>Code Sample</em><br> |
| * <code> |
| * XMLField pictureField = new XMLField("picture/text()")<br> |
| * pictureField.setSchemaType(XMLConstants.HEX_BINARY_QNAME);<br> |
| * </code> |
| * |
| * <p><b>Setting custom conversion pairs</b>: By default in TopLink XML built-in schema types are associated with |
| * java classes and vice versa. These default pairs can be modified by the user using the addJavaConversion and |
| * addXMLConversion api. For example by default a java.util.Calendar is mapped to the dateTime schema type |
| * so the XML will be formated based on that type. Below are the default schema type to java type conversion pairs |
| * and the default java type to schema type conversion pairs. |
| * <table border="1"> |
| * <caption>XML schema type to Java type default conversion pairs</caption> |
| * <tr> |
| * <th id="c3">Schema Type</th> |
| * <th id="c4">Java Type</th> |
| * </tr> |
| * <tr> |
| * <td headers="c3">base64Binary</td> |
| * <td headers="c4">byte[]</td> |
| * </tr> |
| * <tr> |
| * <td headers="c3">boolean</td> |
| * <td headers="c4">boolean</td> |
| * </tr> |
| * <tr> |
| * <td headers="c3">byte</td> |
| * <td headers="c4">byte</td> |
| * </tr> |
| * <tr> |
| * <td headers="c3">date</td> |
| * <td headers="c4">java.util.Calendar</td> |
| * </tr> |
| * <tr> |
| * <td headers="c3">dateTime</td> |
| * <td headers="c4">java.util.Calendar</td> |
| * </tr> |
| * <tr> |
| * <td headers="c3">decimal</td> |
| * <td headers="c4">java.math.BigDecimal</td> |
| * </tr> |
| * <tr> |
| * <td headers="c3">double</td> |
| * <td headers="c4">double</td> |
| * </tr> |
| * <tr> |
| * <td headers="c3">float</td> |
| * <td headers="c4">float</td> |
| * </tr> |
| * <tr> |
| * <td headers="c3">hexBinary</td> |
| * <td headers="c4">byte[]</td> |
| * </tr> |
| * <tr> |
| * <td headers="c3">int</td> |
| * <td headers="c4">int</td> |
| * </tr> |
| * <tr> |
| * <td headers="c3">integer</td> |
| * <td headers="c4">java.math.BigInteger</td> |
| * </tr> |
| * <tr> |
| * <td headers="c3">long</td> |
| * <td headers="c4">long</td> |
| * </tr> |
| * <tr> |
| * <td headers="c3">QName</td> |
| * <td headers="c4">javax.xml.namespace.QName</td> |
| * </tr> |
| * <tr> |
| * <td headers="c3">time</td> |
| * <td headers="c4">java.util.Calendar</td> |
| * </tr> |
| * <tr> |
| * <td headers="c3">unsignedByte</td> |
| * <td headers="c4">short</td> |
| * </tr> |
| * <tr> |
| * <td headers="c3">unsignedInt</td> |
| * <td headers="c4">long</td> |
| * </tr> |
| * <tr> |
| * <td headers="c3">unsignedShort</td> |
| * <td headers="c4">int</td> |
| * </tr> |
| * <tr> |
| * <td headers="c3">anySimpleType</td> |
| * <td headers="c4">java.lang.String</td> |
| * </tr> |
| * </table> |
| * |
| * <table border="1"> |
| * <caption>Java type to XML schema type default conversion pairs</caption> |
| * <tr> |
| * <th id="c5">Java Type</th> |
| * <th id="c6">Schema Type</th> |
| * </tr> |
| * <tr> |
| * <td headers="c5">byte[]</td> |
| * <td headers="c6">hexBinary</td> |
| * </tr> |
| * <tr> |
| * <td headers="c5">java.lang.Byte[]</td> |
| * <td headers="c6">hexBinary</td> |
| * </tr> |
| * <tr> |
| * <td headers="c5">java.math.BigDecimal</td> |
| * <td headers="c6">decimal</td> |
| * </tr> |
| * <tr> |
| * <td headers="c5">java.math.BigInteger</td> |
| * <td headers="c6">integer</td> |
| * </tr> |
| * <tr> |
| * <td headers="c5">boolean</td> |
| * <td headers="c6">boolean</td> |
| * </tr> |
| * <tr> |
| * <td headers="c5">java.lang.Boolean</td> |
| * <td headers="c6">boolean</td> |
| * </tr> |
| * <tr> |
| * <td headers="c5">java.lang.Byte</td> |
| * <td headers="c6">Byte</td> |
| * </tr> |
| * <tr> |
| * <td headers="c5">byte</td> |
| * <td headers="c6">byte</td> |
| * </tr> |
| * <tr> |
| * <td headers="c5">java.util.Calendar</td> |
| * <td headers="c6">dateTime</td> |
| * </tr> |
| * <tr> |
| * <td headers="c5">java.util.GregorianCalendar</td> |
| * <td headers="c6">dateTime</td> |
| * </tr> |
| * <tr> |
| * <td headers="c5">double</td> |
| * <td headers="c6">double</td> |
| * </tr> |
| * <tr> |
| * <td headers="c5">java.lang.Double</td> |
| * <td headers="c6">double</td> |
| * </tr> |
| * <tr> |
| * <td headers="c5">float</td> |
| * <td headers="c6">float</td> |
| * </tr> |
| * <tr> |
| * <td headers="c5">java.lang.Float</td> |
| * <td headers="c6">float</td> |
| * </tr> |
| * <tr> |
| * <td headers="c5">int</td> |
| * <td headers="c6">int</td> |
| * </tr> |
| * <tr> |
| * <td headers="c5">java.lang.Integer</td> |
| * <td headers="c6">int</td> |
| * </tr> |
| * <tr> |
| * <td headers="c5">long</td> |
| * <td headers="c6">long</td> |
| * </tr> |
| * <tr> |
| * <td headers="c5">java.lang.Long</td> |
| * <td headers="c6">long</td> |
| * </tr> |
| * <tr> |
| * <td headers="c5">short</td> |
| * <td headers="c6">short</td> |
| * </tr> |
| * <tr> |
| * <td headers="c5">java.lang.Short</td> |
| * <td headers="c6">short</td> |
| * </tr> |
| * <tr> |
| * <td headers="c5">javax.xml.namespace.QName</td> |
| * <td headers="c6">QName</td> |
| * </tr> |
| * <tr> |
| * <td headers="c5">java.lang.String</td> |
| * <td headers="c6">string</td> |
| * </tr> |
| * </table> |
| * @see org.eclipse.persistence.oxm.XMLUnionField |
| */ |
| public class XMLField extends DatabaseField implements Field<XMLConversionManager, NamespaceResolver> { |
| private NamespaceResolver namespaceResolver; |
| private QName schemaType; |
| private XPathFragment xPathFragment; |
| private XPathFragment lastXPathFragment; |
| private boolean isCDATA = false; |
| private boolean isRequired = false; |
| private boolean isInitialized = false; |
| private boolean nestedArray = false; |
| |
| /** Makes this maintain the collection of items in a single attribute or element instead of having one element per item in the collection. |
| * Default is false */ |
| private boolean usesSingleNode; |
| |
| // Hashtables for the non-default conversions, these Hashtables are |
| // used if the user has made any modifications to the pairs |
| protected HashMap userXMLTypes; |
| protected HashMap userJavaTypes; |
| protected boolean isTypedTextField; |
| protected QName leafElementType; |
| |
| /** |
| * Default constructor, create a new XMLField |
| */ |
| public XMLField() { |
| super(); |
| isTypedTextField = false; |
| } |
| |
| /** |
| * Default constructor, create a new XMLField based on the specified xPath |
| * @param xPath The xPath statement for this field |
| */ |
| public XMLField(String xPath) { |
| super(xPath, new DatabaseTable()); |
| isTypedTextField = false; |
| } |
| |
| @Override |
| public void initialize() { |
| if(null != xPathFragment) { |
| initializeXPathFragment(xPathFragment); |
| } |
| isInitialized = true; |
| } |
| |
| private void initializeXPathFragment(XPathFragment xPathFragment) { |
| XPathPredicate predicate = xPathFragment.getPredicate(); |
| if(null != predicate) { |
| initializeXPathFragment(predicate.getXPathFragment()); |
| } |
| |
| String localName = xPathFragment.getLocalName(); |
| if(localName !=null && !localName.equals(XMLConstants.EMPTY_STRING)){ |
| if(null == xPathFragment.getNamespaceURI()) { |
| if(xPathFragment.hasNamespace()) { |
| if(null == namespaceResolver) { |
| throw XMLMarshalException.namespaceNotFound(xPathFragment.getShortName()); |
| } else { |
| String uri = namespaceResolver.resolveNamespacePrefix(xPathFragment.getPrefix()); |
| if(null == uri && null != xPathFragment.getPrefix()) { |
| throw XMLMarshalException.namespaceNotFound(xPathFragment.getShortName()); |
| } |
| xPathFragment.setNamespaceURI(uri); |
| } |
| } |
| else if(!xPathFragment.isAttribute() && null != namespaceResolver) { |
| xPathFragment.setNamespaceURI(namespaceResolver.getDefaultNamespaceURI()); |
| } |
| } |
| } |
| XPathFragment nextXPathFragment = xPathFragment.getNextFragment(); |
| if(null != nextXPathFragment) { |
| initializeXPathFragment(nextXPathFragment); |
| } |
| } |
| |
| /** |
| * Returns the xpath statement associated with this XMLField |
| * @return The xpath statement associated with this XMLField |
| */ |
| @Override |
| public String getXPath() { |
| return getName(); |
| } |
| |
| /** |
| * Set the xpath statment for this XMLField. |
| * @param xPath The xpath statement to be associated with this XMLField |
| */ |
| @Override |
| public void setXPath(String xPath) { |
| setName(xPath); |
| } |
| |
| /** |
| * Get the NamespaceResolver associated with this XMLField |
| * @return The NamespaceResolver associated with this XMLField |
| * @see org.eclipse.persistence.oxm.NamespaceResolver |
| */ |
| @Override |
| public NamespaceResolver getNamespaceResolver() { |
| return namespaceResolver; |
| } |
| |
| /** |
| * Set the NamespaceResolver associated with this XMLField |
| * @param newNamespaceResolver The namespaceResolver to be associated with this XMLField |
| * @see org.eclipse.persistence.oxm.NamespaceResolver |
| */ |
| @Override |
| public void setNamespaceResolver(NamespaceResolver newNamespaceResolver) { |
| namespaceResolver = newNamespaceResolver; |
| } |
| |
| /** |
| * PUBLIC: |
| * Sets whether the mapping uses a single node. |
| * @param usesSingleNode True if the items in the collection are in a single node or false if each of the items in the collection is in its own node |
| */ |
| @Override |
| public void setUsesSingleNode(boolean usesSingleNode) { |
| this.usesSingleNode = usesSingleNode; |
| } |
| |
| /** |
| * PUBLIC: |
| * Checks whether the mapping uses a single node. |
| * |
| * @return True if the items in the collection are in a single node or false if each of the items in the collection is in its own node. |
| */ |
| @Override |
| public boolean usesSingleNode() { |
| return usesSingleNode; |
| } |
| |
| /** |
| * Sets the schematype associated with this XMLField |
| * This is an optional setting; when set the schema type will be used to format the XML appropriately |
| * @param value QName to be added to the list of schema types |
| */ |
| @Override |
| public void setSchemaType(QName value) { |
| this.schemaType = value; |
| } |
| |
| /** |
| * Return the schema type associated with this field |
| * @return the schema type |
| */ |
| @Override |
| public QName getSchemaType() { |
| return schemaType; |
| } |
| |
| /** |
| * Returns if the field is a typed text field |
| * True when we should base conversions on the "type" attribute on elements |
| * @return True when we should base conversions on the "type" attribute on elements, otherwise false |
| */ |
| @Override |
| public boolean isTypedTextField() { |
| return isTypedTextField; |
| } |
| |
| /** |
| * Set if the field is a typed text field |
| * True when we should base conversions on the "type" attribute on elements |
| * @param value The boolean value specifiy if this is a typed text field |
| */ |
| @Override |
| public void setIsTypedTextField(boolean value) { |
| isTypedTextField = value; |
| } |
| |
| /** |
| * INTERNAL: |
| * Indicates if the xpath for this field is "." |
| * |
| * @return true if the xpath is ".", false otherwise |
| */ |
| @Override |
| public boolean isSelfField() { |
| if (null == xPathFragment) { |
| return false; |
| } |
| return xPathFragment.isSelfFragment(); |
| } |
| |
| /** |
| * INTERNAL: |
| * Returns false since this is a union field |
| * The subclass XMLUnionField returns true for this |
| */ |
| @Override |
| public boolean isUnionField() { |
| return false; |
| } |
| |
| /** |
| * Override setName in superclass |
| */ |
| @Override |
| public void setName(String xPath, String startDelimiter, String endDelimiter) { |
| super.setName(xPath, null, null); |
| |
| if (hasPath(xPath)) { |
| buildFragments(xPath); |
| } else { |
| XPathFragment xPathFragment = new XPathFragment(xPath.intern()); |
| xPathFragment.setXMLField(this); |
| setXPathFragment(xPathFragment); |
| setLastXPathFragment(xPathFragment); |
| } |
| } |
| |
| /** |
| * This has the same effect as calling the setXPath method |
| * |
| * @param xPath The xPath associated with this XMLField |
| */ |
| @Override |
| public void setName(String xPath) { |
| setName(xPath, null, null); |
| } |
| |
| private boolean hasPath(String xpathString) { |
| return ((xpathString != null) && (xpathString.indexOf('/') != -1)); |
| } |
| |
| private void buildFragments(String xpathString) { |
| StringTokenizer st = new StringTokenizer(xpathString, "/", true); |
| String next; |
| int i = 0; |
| XPathFragment currentXPathFragment = null; |
| XPathFragment nextXPathFragment = null; |
| |
| while (st.hasMoreTokens()) { |
| next = st.nextToken(); |
| if (null != next) { |
| if ("/".equals(next)) { |
| if (0 == i) { |
| next = "/" + st.nextToken(); |
| } else { |
| continue; |
| } |
| } |
| if (next.contains("[") && !next.contains("]")) { |
| StringBuilder sb = new StringBuilder(next); |
| String more; |
| while (st.hasMoreTokens()) { |
| more = st.nextToken(); |
| sb.append(more); |
| if (more.contains("]")) |
| break; |
| } |
| next = sb.toString().intern(); |
| } else { |
| next = next.intern(); |
| } |
| nextXPathFragment = new XPathFragment(next); |
| if (0 == i) { |
| setXPathFragment(nextXPathFragment); |
| } else { |
| currentXPathFragment.setNextFragment(nextXPathFragment); |
| if (nextXPathFragment.isAttribute() || nextXPathFragment.nameIsText()) { |
| currentXPathFragment.setHasText(true); |
| } |
| } |
| nextXPathFragment.setXMLField(this); |
| currentXPathFragment = nextXPathFragment; |
| i++; |
| } |
| setLastXPathFragment(currentXPathFragment); |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * Maintain a direct pointer to the first XPathFragment. For example given |
| * the following XPath first/middle/@last, first is the first XPathFragment. |
| */ |
| @Override |
| public XPathFragment getXPathFragment() { |
| return xPathFragment; |
| } |
| |
| /** |
| * INTERNAL: |
| * Return the first XPathFragment. |
| */ |
| public void setXPathFragment(XPathFragment xPathFragment) { |
| this.xPathFragment = xPathFragment; |
| } |
| |
| /** |
| * INTERNAL: |
| * Return the last XPathFragment. |
| */ |
| @Override |
| public XPathFragment getLastXPathFragment() { |
| return lastXPathFragment; |
| } |
| |
| /** |
| * INTERNAL: |
| * Maintain a direct pointer to the last XPathFragment. For example given |
| * the following XPath first/middle/@last, @last is the last XPathFragment. |
| */ |
| public void setLastXPathFragment(XPathFragment lastXPathFragment) { |
| this.lastXPathFragment = lastXPathFragment; |
| } |
| |
| /** |
| * INTERNAL |
| * Return the class for a given qualified XML Schema type |
| * @param qname The qualified name of the XML Schema type to use as a key in the lookup |
| * @return The class corresponding to the specified schema type, if no corresponding match found returns null |
| */ |
| public Class<?> getJavaClass(QName qname) { |
| return getJavaClass(qname, XMLConversionManager.getDefaultXMLManager()); |
| } |
| |
| /** |
| * INTERNAL |
| * @return the class for a given qualified XML Schema type. |
| * @since EclipseLink 2.6.0 |
| */ |
| @Override |
| public Class<?> getJavaClass(QName qname, ConversionManager conversionManager) { |
| if (userXMLTypes != null) { |
| Class<?> theClass = (Class)userXMLTypes.get(qname); |
| if(theClass != null){ |
| return theClass; |
| } |
| } |
| return conversionManager.javaType(qname); |
| } |
| |
| /** |
| * Return the qualified XML Schema type for a given class |
| * @param javaClass The class to use as a key in the lookup |
| * @return QName The qualified XML Schema type, if no corresponding match found returns null |
| */ |
| public QName getXMLType(Class<?> javaClass) { |
| return getXMLType(javaClass, XMLConversionManager.getDefaultXMLManager()); |
| } |
| |
| /** |
| * @return the XML Schema type for a given class. |
| * @since EclipseLink 2.6.0 |
| */ |
| @Override |
| public QName getXMLType(Class<?> javaClass, ConversionManager conversionManager) { |
| if (userJavaTypes != null) { |
| QName theQName = (QName)userJavaTypes.get(javaClass); |
| if (theQName !=null) { |
| return theQName; |
| } |
| } |
| |
| return conversionManager.schemaType(javaClass); |
| } |
| |
| /** |
| * @return a HashMap of Java to XML Schema type conversion pairs |
| */ |
| private HashMap getUserJavaTypes() { |
| // If no manual modifications have been made to the conversion pairs yet, |
| // userJavaTypes will be null and needs to be built |
| if (userJavaTypes == null) { |
| userJavaTypes = new HashMap(); |
| } |
| return userJavaTypes; |
| } |
| |
| /** |
| * @return a HashMap of XML Schema types to Java types conversion pairs |
| */ |
| private HashMap getUserXMLTypes() { |
| // If no manual modifications have been made to the conversion pairs yet, |
| // userXMLTypes will be null and needs to be built |
| if (userXMLTypes == null) { |
| userXMLTypes = new HashMap(); |
| } |
| return userXMLTypes; |
| } |
| |
| /** |
| * INTERNAL: |
| */ |
| public ArrayList getUserXMLTypesForDeploymentXML() { |
| if (userXMLTypes != null) { |
| ArrayList types = new ArrayList(userXMLTypes.size()); |
| Iterator iter = userXMLTypes.entrySet().iterator(); |
| while (iter.hasNext()) { |
| Map.Entry entry = (Map.Entry)iter.next(); |
| XMLConversionPair pair = new XMLConversionPair((QName)entry.getKey(),((Class)entry.getValue()).getName()); |
| types.add(pair); |
| } |
| return types; |
| } |
| return null; |
| } |
| |
| /** |
| * INTERNAL: |
| */ |
| public void setUserXMLTypesForDeploymentXML(ArrayList pairs) throws Exception { |
| if (pairs.size() > 0) { |
| userXMLTypes = new HashMap(); |
| Iterator iter = pairs.iterator(); |
| while (iter.hasNext()) { |
| XMLConversionPair pair = (XMLConversionPair)iter.next(); |
| if ((pair.getXmlType() != null) && (pair.getJavaType() != null)) { |
| userXMLTypes.put(pair.getXmlType(), Class.forName(pair.getJavaType())); |
| } |
| } |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| */ |
| public ArrayList getUserJavaTypesForDeploymentXML() { |
| if (userJavaTypes != null) { |
| ArrayList types = new ArrayList(userJavaTypes.size()); |
| Iterator iter = userJavaTypes.entrySet().iterator(); |
| while (iter.hasNext()) { |
| Map.Entry entry = (Map.Entry)iter.next(); |
| XMLConversionPair pair = new XMLConversionPair((QName)entry.getValue(), ((Class)entry.getKey()).getName()); |
| types.add(pair); |
| } |
| return types; |
| } |
| return null; |
| } |
| |
| /** |
| * INTERNAL: |
| */ |
| public void setUserJavaTypesForDeploymentXML(ArrayList pairs) throws Exception { |
| if (pairs.size() > 0) { |
| userJavaTypes = new HashMap(); |
| Iterator iter = pairs.iterator(); |
| while (iter.hasNext()) { |
| XMLConversionPair pair = (XMLConversionPair)iter.next(); |
| if ((pair.getXmlType() != null) && (pair.getJavaType() != null)) { |
| userJavaTypes.put(Class.forName(pair.getJavaType()), pair.getXmlType()); |
| } |
| } |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * Called from DOMRecord and XMLReader. MappingNodeValues call XMLReader which calls this method so that other XMLReader subclasses can override. |
| */ |
| @Override |
| public Object convertValueBasedOnSchemaType(Object value, XMLConversionManager xmlConversionManager, AbstractUnmarshalRecord record) { |
| if (schemaType != null) { |
| if(XMLConstants.QNAME_QNAME.equals(schemaType)){ |
| return xmlConversionManager.buildQNameFromString((String)value, record); |
| }else{ |
| Class<?> fieldType = getType(); |
| if (fieldType == null) { |
| fieldType = getJavaClass(schemaType, xmlConversionManager); |
| } |
| return xmlConversionManager.convertObject(value, fieldType, schemaType); |
| } |
| } |
| return value; |
| } |
| |
| |
| /** |
| * Add an XML to Java Conversion pair entry |
| * @param qname The qualified name of the XML schema type |
| * @param javaClass The class to add |
| */ |
| public void addXMLConversion(QName qname, Class<?> javaClass) { |
| getUserXMLTypes().put(qname, javaClass); |
| } |
| |
| /** |
| * Add a Java to XML Conversion pair entry |
| * @param javaClass The class to add |
| * @param qname The qualified name of the XML schema type |
| */ |
| public void addJavaConversion(Class<?> javaClass, QName qname) { |
| getUserJavaTypes().put(javaClass, qname); |
| } |
| |
| /** |
| * Add an entry for both an XML Conversion and a Java Conversion entry |
| * @param qname The qualified name of the XML schema type |
| */ |
| public void addConversion(QName qname, Class<?> javaClass) { |
| addJavaConversion(javaClass, qname); |
| addXMLConversion(qname, javaClass); |
| } |
| |
| /** |
| * Remove an XML to Java Conversion entry |
| */ |
| public void removeXMLConversion(QName qname) { |
| getUserXMLTypes().remove(qname); |
| } |
| |
| /** |
| * Remove a Java to XML Conversion entry |
| * |
| */ |
| public void removeJavaConversion(Class<?> javaClass) { |
| getUserJavaTypes().remove(javaClass); |
| } |
| |
| /** |
| * Remove both a Java to XML Conversion and the corresponding XML to Java Conversion entry |
| * |
| */ |
| public void removeConversion(QName qname, Class<?> javaClass) { |
| removeJavaConversion(javaClass); |
| removeXMLConversion(qname); |
| } |
| |
| /** |
| * Assumes type is in the format prefix:localPart, or localPart. |
| * |
| */ |
| public void setLeafElementType(QName type) { |
| leafElementType = type; |
| if (hasLastXPathFragment()) { |
| getLastXPathFragment().setLeafElementType(type); |
| } |
| } |
| |
| @Override |
| public QName getLeafElementType() { |
| if (lastXPathFragment != null) { |
| return lastXPathFragment.getLeafElementType(); |
| } |
| return leafElementType; |
| } |
| |
| /** |
| * INTERNAL: |
| */ |
| @Override |
| public boolean hasLastXPathFragment() { |
| return lastXPathFragment != null; |
| } |
| |
| /** |
| * INTERNAL: |
| */ |
| @Override |
| public QName getSchemaTypeForValue(Object value, CoreAbstractSession session) { |
| if(leafElementType != null){ |
| return leafElementType; |
| }else if (isTypedTextField) { |
| if (CoreClassConstants.XML_GREGORIAN_CALENDAR.isAssignableFrom(value.getClass())){ |
| return ((XMLGregorianCalendar) value).getXMLSchemaType(); |
| }else if (CoreClassConstants.DURATION.isAssignableFrom(value.getClass())){ |
| ConversionManager conversionManager = (ConversionManager) session.getDatasourcePlatform().getConversionManager(); |
| return getXMLType(CoreClassConstants.DURATION, conversionManager); |
| } |
| ConversionManager conversionManager = (ConversionManager) session.getDatasourcePlatform().getConversionManager(); |
| return getXMLType(value.getClass(), conversionManager); |
| } |
| return schemaType; |
| } |
| |
| |
| /** |
| * INTERNAL: |
| */ |
| @Override |
| public void setIsCDATA(boolean CDATA) { |
| this.isCDATA = CDATA; |
| } |
| |
| /** |
| * INTERNAL: |
| */ |
| @Override |
| public boolean isCDATA() { |
| return isCDATA; |
| } |
| /** |
| * INTERNAL |
| */ |
| @Override |
| public boolean isSchemaType(QName schemaType){ |
| if(getSchemaType() == null){ |
| return false; |
| } |
| return getSchemaType().equals(schemaType); |
| } |
| |
| /** |
| * Indicates if this XMLField represents a "required" XML element or attribute |
| * ([minOccurs="1"] for elements, [use="required"] for attributes). NOTE: This |
| * API is used only for Schema Generation. |
| * |
| * @see org.eclipse.persistence.internal.oxm.schema.SchemaModelGenerator |
| */ |
| @Override |
| public boolean isRequired() { |
| return isRequired; |
| } |
| |
| /** |
| * Set whether this XMLField represents a "required" XML element or attribute |
| * ([minOccurs="1"] for elements, [use="required"] for attributes). NOTE: This |
| * API is used only for Schema Generation. |
| * |
| * @see org.eclipse.persistence.internal.oxm.schema.SchemaModelGenerator |
| */ |
| @Override |
| public void setRequired(boolean isRequired) { |
| this.isRequired = isRequired; |
| } |
| |
| @Override |
| public boolean equals(Object object) { |
| try { |
| if(!isInitialized) { |
| return super.equals(object); |
| } |
| if(this == object) { |
| return true; |
| } |
| XMLField xmlField = (XMLField) object; |
| if(!xPathFragment.equals(xmlField.getXPathFragment())) { |
| return false; |
| } |
| XPathFragment xpf = xPathFragment; |
| XPathFragment xpf2 = xmlField.getXPathFragment(); |
| while(xpf.getNextFragment() != null) { |
| xpf = xpf.getNextFragment(); |
| xpf2 = xpf2.getNextFragment(); |
| if(!xpf.equals(xpf2)) { |
| return false; |
| } |
| } |
| return null == xpf2.getNextFragment(); |
| } catch(ClassCastException e) { |
| return false; |
| } |
| } |
| |
| /** |
| * Set nested array flag. Used in JSON marshalling. |
| * |
| * @param nestedArray flag. |
| */ |
| @Override |
| public void setNestedArray(boolean nestedArray) { |
| this.nestedArray = nestedArray; |
| } |
| |
| /** |
| * INTERNAL: |
| * |
| * @return True if content is nested array. |
| */ |
| @Override |
| public boolean isNestedArray() { |
| return this.nestedArray; |
| } |
| |
| @Override |
| public int hashCode() { |
| if(null == xPathFragment && null == xPathFragment.getXPath()) { |
| return 1; |
| } |
| return (xPathFragment.getXPath().replace((xPathFragment.getPrefix() != null) ? xPathFragment.getPrefix() + xPathFragment.getNamespaceSeparator() : "", "")).hashCode(); |
| } |
| |
| } |