| /* |
| * 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 |
| // Marcel Valovy - 2.6.0 - added case insensitive unmarshalling |
| package org.eclipse.persistence.internal.oxm.record; |
| |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| |
| import javax.xml.namespace.QName; |
| |
| import org.eclipse.persistence.core.descriptors.CoreDescriptor; |
| import org.eclipse.persistence.core.descriptors.CoreDescriptorEventManager; |
| import org.eclipse.persistence.core.descriptors.CoreInheritancePolicy; |
| import org.eclipse.persistence.core.queries.CoreAttributeGroup; |
| import org.eclipse.persistence.descriptors.DescriptorEvent; |
| import org.eclipse.persistence.descriptors.DescriptorEventManager; |
| import org.eclipse.persistence.exceptions.EclipseLinkException; |
| import org.eclipse.persistence.exceptions.XMLMarshalException; |
| import org.eclipse.persistence.internal.core.helper.CoreField; |
| import org.eclipse.persistence.internal.core.sessions.CoreAbstractRecord; |
| import org.eclipse.persistence.internal.core.sessions.CoreAbstractSession; |
| import org.eclipse.persistence.internal.identitymaps.CacheId; |
| import org.eclipse.persistence.internal.oxm.Constants; |
| import org.eclipse.persistence.internal.oxm.ContainerValue; |
| import org.eclipse.persistence.internal.oxm.ConversionManager; |
| import org.eclipse.persistence.internal.oxm.IDResolver; |
| import org.eclipse.persistence.internal.oxm.MappingNodeValue; |
| import org.eclipse.persistence.internal.oxm.NamespaceResolver; |
| import org.eclipse.persistence.internal.oxm.NodeValue; |
| import org.eclipse.persistence.internal.oxm.NullCapableValue; |
| import org.eclipse.persistence.internal.oxm.ObjectBuilder; |
| import org.eclipse.persistence.internal.oxm.Reference; |
| import org.eclipse.persistence.internal.oxm.ReferenceResolver; |
| import org.eclipse.persistence.internal.oxm.Root; |
| import org.eclipse.persistence.internal.oxm.SAXFragmentBuilder; |
| import org.eclipse.persistence.internal.oxm.StrBuffer; |
| import org.eclipse.persistence.internal.oxm.Unmarshaller; |
| import org.eclipse.persistence.internal.oxm.XPathFragment; |
| import org.eclipse.persistence.internal.oxm.XPathNode; |
| import org.eclipse.persistence.internal.oxm.XPathPredicate; |
| import org.eclipse.persistence.internal.oxm.XPathQName; |
| import org.eclipse.persistence.internal.oxm.mappings.Descriptor; |
| import org.eclipse.persistence.internal.oxm.mappings.DirectMapping; |
| import org.eclipse.persistence.internal.oxm.mappings.Field; |
| import org.eclipse.persistence.internal.oxm.mappings.Mapping; |
| import org.eclipse.persistence.internal.oxm.mappings.TransformationMapping; |
| import org.eclipse.persistence.internal.oxm.record.namespaces.StackUnmarshalNamespaceResolver; |
| import org.eclipse.persistence.internal.oxm.record.namespaces.UnmarshalNamespaceResolver; |
| import org.eclipse.persistence.internal.oxm.unmapped.UnmappedContentHandler; |
| import org.eclipse.persistence.internal.security.PrivilegedNewInstanceFromClass; |
| import org.eclipse.persistence.internal.sessions.AbstractSession; |
| import org.eclipse.persistence.sessions.coordination.CommandProcessor; |
| import org.w3c.dom.Document; |
| import org.xml.sax.Attributes; |
| import org.xml.sax.ErrorHandler; |
| import org.xml.sax.Locator; |
| import org.xml.sax.SAXException; |
| import org.xml.sax.SAXParseException; |
| import org.xml.sax.ext.Locator2; |
| import org.xml.sax.ext.Locator2Impl; |
| |
| /** |
| * <p><b>Purpose:</b>Provide an implementation of ContentHandler that is used by TopLink OXM to |
| * build mapped Java Objects from SAX events.</p> |
| * <p><b>Responsibilities:</b></p> |
| * <ul> |
| * <li>Implement the ContentHandler and LexicalHandler interfaces</li> |
| * <li>Make calls into the appropriate NodeValues based on the incoming SAXEvents</li> |
| * <li>Make callbacks into XMLReader for newObject events</li> |
| * <li>Maintain a map of Collections to be populated for collection mappings.</li> |
| * </ul> |
| * |
| * @see org.eclipse.persistence.internal.oxm.XPathNode |
| * @see org.eclipse.persistence.internal.oxm.NodeValue |
| * @see org.eclipse.persistence.internal.oxm.TreeObjectBuilder |
| * @author bdoughan |
| * |
| */ |
| public class UnmarshalRecordImpl<TRANSFORMATION_RECORD extends TransformationRecord> extends CoreAbstractRecord implements UnmarshalRecord<CoreAbstractSession, CoreField, IDResolver, ObjectBuilder, TRANSFORMATION_RECORD, Unmarshaller> { |
| protected XMLReader xmlReader; |
| private ObjectBuilder treeObjectBuilder; |
| private XPathFragment xPathFragment; |
| private XPathNode xPathNode; |
| /** |
| * Used to increase performance. We are trying to predict next mapping to unmarshal. |
| * It can reduce the number of map lookups. |
| */ |
| private XPathNode predictedNextXPathNode; |
| private int levelIndex; |
| private UnmarshalRecord childRecord; |
| protected UnmarshalRecord parentRecord; |
| private TRANSFORMATION_RECORD transformationRecord; |
| private List<UnmarshalRecord> selfRecords; |
| private Map<XPathFragment, Integer> indexMap; |
| private List<NullCapableValue> nullCapableValues; |
| private Object[] containerInstances; |
| private List<ContainerValue> defaultEmptyContainerValues; |
| private List<ContainerValue> populatedContainerValues; |
| private boolean isBufferCDATA; |
| private Attributes attributes; |
| private QName typeQName; |
| protected String rootElementLocalName; |
| protected String rootElementName; |
| protected String rootElementNamespaceUri; |
| private SAXFragmentBuilder fragmentBuilder; |
| private Map<String, String> prefixesForFragment; |
| private String encoding; |
| private String version; |
| private String schemaLocation; |
| private String noNamespaceSchemaLocation; |
| private boolean isSelfRecord; |
| private UnmarshalContext unmarshalContext; |
| private UnmarshalNamespaceResolver unmarshalNamespaceResolver; |
| private boolean isXsiNil; |
| private boolean xpathNodeIsMixedContent = false; |
| private int unmappedLevel = -1; |
| private ReferenceResolver referenceResolver; |
| |
| |
| protected Unmarshaller unmarshaller; |
| protected Object currentObject; |
| protected CoreAbstractSession session; |
| protected boolean namespaceAware; |
| private XPathQName leafElementType; |
| private NamespaceResolver namespaceResolver; |
| |
| private CoreAttributeGroup unmarshalAttributeGroup; |
| |
| // The "snapshot" location of this object, for @XmlLocation |
| private Locator xmlLocation; |
| |
| protected XPathFragment textWrapperFragment; |
| |
| private ConversionManager conversionManager; |
| |
| protected UnmarshalRecordImpl() { |
| } |
| |
| public UnmarshalRecordImpl(ObjectBuilder objectBuilder) { |
| this(objectBuilder, new ReferenceResolver()); |
| } |
| |
| private UnmarshalRecordImpl(ObjectBuilder objectBuilder, ReferenceResolver referenceResolver) { |
| super(); |
| this.referenceResolver = referenceResolver; |
| this.xPathFragment = new XPathFragment(); |
| xPathFragment.setNamespaceAware(isNamespaceAware()); |
| this.setUnmarshalAttributeGroup(DEFAULT_ATTRIBUTE_GROUP); |
| initialize(objectBuilder); |
| } |
| |
| @Override |
| public UnmarshalRecord initialize(ObjectBuilder treeObjectBuilder) { |
| this.isBufferCDATA = false; |
| this.treeObjectBuilder = treeObjectBuilder; |
| if (null != treeObjectBuilder) { |
| this.xPathNode = treeObjectBuilder.getRootXPathNode(); |
| if (null != treeObjectBuilder.getNullCapableValues()) { |
| this.nullCapableValues = new ArrayList<NullCapableValue>(treeObjectBuilder.getNullCapableValues()); |
| } |
| if (null != treeObjectBuilder.getDefaultEmptyContainerValues()){ |
| this.defaultEmptyContainerValues = new ArrayList<ContainerValue>(treeObjectBuilder.getDefaultEmptyContainerValues()); |
| } |
| } |
| isSelfRecord = false; |
| return this; |
| } |
| |
| private void reset() { |
| xPathNode = null; |
| childRecord = null; |
| transformationRecord = null; |
| if(null != selfRecords) { |
| selfRecords.clear(); |
| } |
| if(null != indexMap) { |
| indexMap.clear(); |
| } |
| nullCapableValues = null; |
| isBufferCDATA = false; |
| attributes = null; |
| typeQName = null; |
| isSelfRecord = false; |
| unmarshalContext = null; |
| isXsiNil = false; |
| unmappedLevel = -1; |
| predictedNextXPathNode = null; |
| } |
| |
| @Override |
| public String getLocalName() { |
| return rootElementLocalName; |
| } |
| |
| @Override |
| public void setLocalName(String localName) { |
| rootElementLocalName = localName; |
| } |
| |
| public String getNamespaceURI() { |
| throw XMLMarshalException.operationNotSupported("getNamespaceURI"); |
| } |
| |
| public void clear() { |
| throw XMLMarshalException.operationNotSupported("clear"); |
| } |
| |
| public Document getDocument() { |
| throw XMLMarshalException.operationNotSupported("getDocument"); |
| } |
| |
| public String transformToXML() { |
| throw XMLMarshalException.operationNotSupported("transformToXML"); |
| } |
| |
| @Override |
| public XMLReader getXMLReader() { |
| return this.xmlReader; |
| } |
| |
| @Override |
| public void setXMLReader(XMLReader xmlReader) { |
| this.xmlReader = xmlReader; |
| namespaceAware = xmlReader.isNamespaceAware(); |
| if(xPathFragment != null){ |
| xPathFragment.setNamespaceAware(isNamespaceAware()); |
| } |
| } |
| |
| @Override |
| public UnmarshalRecord getChildRecord() { |
| return this.childRecord; |
| } |
| |
| @Override |
| public void setChildRecord(UnmarshalRecord childRecord) { |
| this.childRecord = childRecord; |
| if (null != childRecord) { |
| childRecord.setParentRecord(this); |
| } |
| } |
| |
| @Override |
| public UnmarshalRecord getParentRecord() { |
| return this.parentRecord; |
| } |
| |
| /** |
| * INTERNAL: |
| * The ReferenceResolver that is leveraged by key based mappings. |
| * @since EclipseLink 2.5.0 |
| */ |
| @Override |
| public ReferenceResolver getReferenceResolver() { |
| if(null == referenceResolver) { |
| referenceResolver = new ReferenceResolver(); |
| } |
| return referenceResolver; |
| } |
| |
| /** |
| * INTERNAL: |
| * Set the ReferenceResolver that will be leveraged by key based mappings. |
| * @since EclipseLink 2.5.0 |
| */ |
| @Override |
| public void setReferenceResolver(ReferenceResolver referenceResolver) { |
| this.referenceResolver = referenceResolver; |
| } |
| |
| /** |
| * Return the root element's prefix qualified name |
| */ |
| @Override |
| public String getRootElementName() { |
| return rootElementName; |
| } |
| |
| @Override |
| public void setRootElementName(String qName) { |
| this.rootElementName = qName; |
| } |
| |
| /** |
| * Return the root element's namespace URI |
| */ |
| @Override |
| public String getRootElementNamespaceUri() { |
| return rootElementNamespaceUri; |
| } |
| |
| @Override |
| public void setRootElementNamespaceUri(String uri) { |
| this.rootElementNamespaceUri = uri; |
| } |
| |
| @Override |
| public void setParentRecord(UnmarshalRecord parentRecord) { |
| this.parentRecord = parentRecord; |
| } |
| |
| @Override |
| public TRANSFORMATION_RECORD getTransformationRecord() { |
| return this.transformationRecord; |
| } |
| |
| @Override |
| public void setTransformationRecord(TRANSFORMATION_RECORD transformationRecord) { |
| this.transformationRecord = transformationRecord; |
| } |
| |
| @Override |
| public UnmarshalNamespaceResolver getUnmarshalNamespaceResolver() { |
| if(null == unmarshalNamespaceResolver) { |
| this.unmarshalNamespaceResolver = new StackUnmarshalNamespaceResolver(); |
| } |
| return this.unmarshalNamespaceResolver; |
| } |
| |
| @Override |
| public void setUnmarshalNamespaceResolver(UnmarshalNamespaceResolver anUnmarshalNamespaceResolver) { |
| this.unmarshalNamespaceResolver = anUnmarshalNamespaceResolver; |
| } |
| |
| @Override |
| public List getNullCapableValues() { |
| if (null == nullCapableValues) { |
| this.nullCapableValues = new ArrayList<>(); |
| } |
| return this.nullCapableValues; |
| } |
| |
| @Override |
| public void removeNullCapableValue(NullCapableValue nullCapableValue) { |
| if(null != nullCapableValues) { |
| nullCapableValues.remove(nullCapableValue); |
| } |
| } |
| |
| @Override |
| public Object getContainerInstance(ContainerValue c) { |
| return getContainerInstance(c, true); |
| } |
| |
| @Override |
| public Object getContainerInstance(ContainerValue c, boolean createContainerIfNecessary) { |
| Object containerInstance = containerInstances[c.getIndex()]; |
| |
| if (containerInstance == null) { |
| Mapping mapping = c.getMapping(); |
| //don't attempt to do a get on a readOnly property. |
| if(c.getReuseContainer() && !(mapping.isReadOnly())) { |
| containerInstance = mapping.getAttributeValueFromObject(currentObject); |
| } |
| if(null == containerInstance && createContainerIfNecessary) { |
| containerInstance = c.getContainerInstance(); |
| } |
| containerInstances[c.getIndex()] = containerInstance; |
| populatedContainerValues.add(c); |
| if(defaultEmptyContainerValues != null){ |
| defaultEmptyContainerValues.remove(c); |
| } |
| } |
| |
| return containerInstance; |
| } |
| |
| @Override |
| public void setContainerInstance(int index, Object containerInstance) { |
| containerInstances[index] = containerInstance; |
| } |
| |
| /** |
| * PUBLIC: |
| * Gets the encoding for this document. Only set on the root-level UnmarshalRecord |
| * @return a String representing the encoding for this doc |
| */ |
| @Override |
| public String getEncoding() { |
| return encoding; |
| } |
| |
| /** |
| * INTERNAL: |
| */ |
| public void setEncoding(String enc) { |
| this.encoding = enc; |
| } |
| |
| /** |
| * PUBLIC: |
| * Gets the XML Version for this document. Only set on the root-level |
| * UnmarshalRecord, if supported by the parser. |
| */ |
| @Override |
| public String getVersion() { |
| return version; |
| } |
| |
| /** |
| * INTERNAL: |
| */ |
| public void setVersion(String version) { |
| this.version = version; |
| } |
| |
| @Override |
| public String getSchemaLocation() { |
| return schemaLocation; |
| } |
| |
| public void setSchemaLocation(String schemaLocation) { |
| this.schemaLocation = schemaLocation; |
| } |
| |
| @Override |
| public String getNoNamespaceSchemaLocation() { |
| return noNamespaceSchemaLocation; |
| } |
| |
| public void setNoNamespaceSchemaLocation(String location) { |
| this.noNamespaceSchemaLocation = location; |
| } |
| |
| protected StrBuffer getStringBuffer() { |
| return unmarshaller.getStringBuffer(); |
| } |
| |
| @Override |
| public CharSequence getCharacters() { |
| return unmarshaller.getStringBuffer(); |
| } |
| |
| @Override |
| public Attributes getAttributes() { |
| return this.attributes; |
| } |
| |
| @Override |
| public void setAttributes(Attributes attributes) { |
| this.attributes = attributes; |
| } |
| |
| @Override |
| public QName getTypeQName() { |
| return this.typeQName; |
| } |
| |
| @Override |
| public void setTypeQName(QName typeQName) { |
| this.typeQName = typeQName; |
| } |
| |
| @Override |
| public void setDocumentLocator(Locator locator) { |
| if(xmlReader != null){ |
| xmlReader.setLocator(locator); |
| if (null == rootElementName && null == rootElementLocalName && parentRecord == null && locator instanceof Locator2){ |
| Locator2 loc = (Locator2)locator; |
| this.setEncoding(loc.getEncoding()); |
| this.setVersion(loc.getXMLVersion()); |
| } |
| } |
| } |
| |
| public Locator getDocumentLocator() { |
| if(xmlReader != null){ |
| return xmlReader.getLocator(); |
| } |
| return null; |
| } |
| |
| @Override |
| public Object get(CoreField key) { |
| Field xmlField = this.convertToXMLField(key); |
| XPathFragment lastFragment = xmlField.getLastXPathFragment(); |
| String namespaceURI = lastFragment.getNamespaceURI(); |
| if(namespaceURI == null){ |
| NamespaceResolver namespaceResolver = xmlField.getNamespaceResolver(); |
| namespaceURI = Constants.EMPTY_STRING; |
| if (null != namespaceResolver && !(lastFragment.isAttribute() && lastFragment.getPrefix() == null)) { |
| namespaceURI = namespaceResolver.resolveNamespacePrefix(lastFragment.getPrefix()); |
| if (null == namespaceURI) { |
| namespaceURI = Constants.EMPTY_STRING; |
| } |
| } |
| } |
| if(isNamespaceAware()){ |
| return attributes.getValue(namespaceURI, lastFragment.getLocalName()); |
| } |
| return attributes.getValue(lastFragment.getLocalName()); |
| } |
| |
| |
| @Override |
| public XPathNode getXPathNode() { |
| return xPathNode; |
| } |
| |
| @Override |
| public Descriptor getDescriptor() { |
| return (Descriptor) treeObjectBuilder.getDescriptor(); |
| } |
| |
| @Override |
| public UnmarshalContext getUnmarshalContext() { |
| return unmarshalContext; |
| } |
| |
| @Override |
| public void setUnmarshalContext(UnmarshalContext unmarshalContext) { |
| this.unmarshalContext = unmarshalContext; |
| } |
| |
| @Override |
| public boolean isNil() { |
| return this.isXsiNil; |
| } |
| |
| @Override |
| public void setNil(boolean nil) { |
| this.isXsiNil = nil; |
| } |
| |
| @Override |
| public void startDocument() throws SAXException { |
| if (unmarshaller.getIDResolver() != null && parentRecord == null) { |
| unmarshaller.getIDResolver().startDocument(unmarshaller.getErrorHandler()); |
| } |
| } |
| |
| private void initializeRecord(Attributes attrs) throws SAXException{ |
| this.setAttributes(attrs); |
| Descriptor xmlDescriptor = (Descriptor) treeObjectBuilder.getDescriptor(); |
| if(!xmlDescriptor.hasInheritance() || xmlDescriptor.getInheritancePolicy().getClassIndicatorField() == null){ |
| initialize((ObjectBuilder)xmlDescriptor.getObjectBuilder()); |
| initializeRecord((Mapping)null); |
| return; |
| } |
| CoreInheritancePolicy inheritancePolicy = xmlDescriptor.getInheritancePolicy(); |
| Class classValue = treeObjectBuilder.classFromRow(this, 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) { |
| XPathQName xpathQName = new XPathQName(leafElementType, isNamespaceAware()); |
| Object indicator = inheritancePolicy.getClassIndicatorMapping().get(xpathQName); |
| if(indicator != null) { |
| classValue = (Class)indicator; |
| } |
| } |
| } |
| if (classValue != null) { |
| xmlDescriptor = (Descriptor)session.getDescriptor(classValue); |
| } |
| initialize((ObjectBuilder)xmlDescriptor.getObjectBuilder()); |
| initializeRecord((Mapping)null); |
| } |
| |
| @Override |
| public void initializeRecord(Mapping selfRecordMapping) throws SAXException { |
| try { |
| Descriptor xmlDescriptor = (Descriptor) treeObjectBuilder.getDescriptor(); |
| if(xmlDescriptor.isSequencedObject()) { |
| unmarshalContext = new SequencedUnmarshalContext(); |
| } else { |
| unmarshalContext = ObjectUnmarshalContext.getInstance(); |
| } |
| |
| currentObject = this.xmlReader.getCurrentObject(session, selfRecordMapping); |
| if (currentObject == null) { |
| currentObject = treeObjectBuilder.buildNewInstance(); |
| } |
| if (xmlDescriptor.getLocationAccessor() != null && xmlReader.getLocator() != null){ |
| // Check to see if this Descriptor isLocationAware |
| // Store the snapshot of the current documentLocator |
| xmlLocation = new Locator2Impl(xmlReader.getLocator()); |
| } |
| |
| Object parentRecordCurrentObject = null; |
| if (null != this.parentRecord) { |
| parentRecordCurrentObject = parentRecord.getCurrentObject(); |
| } |
| |
| Unmarshaller.Listener xmlUnmarshalListener = unmarshaller.getUnmarshalListener(); |
| if (null != xmlUnmarshalListener) { |
| if (null == this.parentRecord) { |
| xmlUnmarshalListener.beforeUnmarshal(currentObject, null); |
| } else { |
| xmlUnmarshalListener.beforeUnmarshal(currentObject, parentRecordCurrentObject); |
| } |
| } |
| if (null == parentRecord) { |
| this.xmlReader.newObjectEvent(currentObject, null, selfRecordMapping); |
| } else { |
| this.xmlReader.newObjectEvent(currentObject, parentRecordCurrentObject, selfRecordMapping); |
| } |
| List containerValues = treeObjectBuilder.getContainerValues(); |
| if (null != containerValues) { |
| int containerSize = containerValues.size(); |
| containerInstances = new Object[containerSize]; |
| populatedContainerValues = new ArrayList(containerSize); |
| } |
| |
| if (null != xPathNode.getSelfChildren()) { |
| int selfChildrenSize = xPathNode.getSelfChildren().size(); |
| selfRecords = new ArrayList<>(selfChildrenSize); |
| for (int x = 0; x < selfChildrenSize; x++) { |
| NodeValue nv = xPathNode.getSelfChildren().get(x).getNodeValue(); |
| if (null != nv) { |
| selfRecords.add(nv.buildSelfRecord(this, attributes)); |
| } |
| } |
| } |
| } catch (EclipseLinkException e) { |
| if (null == xmlReader.getErrorHandler()) { |
| throw e; |
| } else { |
| SAXParseException saxParseException = new SAXParseException(null, getDocumentLocator(), e); |
| xmlReader.getErrorHandler().error(saxParseException); |
| } |
| } |
| } |
| |
| @Override |
| public void endDocument() throws SAXException { |
| if (unmarshaller.getIDResolver() != null && parentRecord == null) { |
| unmarshaller.getIDResolver().endDocument(); |
| } |
| if (null != selfRecords) { |
| for (int x = 0, selfRecordsSize = selfRecords.size(); x < selfRecordsSize; x++) { |
| UnmarshalRecord selfRecord = selfRecords.get(x); |
| if(selfRecord != null){ |
| selfRecord.endDocument(); |
| } |
| } |
| } |
| |
| if (null != xPathNode.getSelfChildren()) { |
| int selfChildrenSize = xPathNode.getSelfChildren().size(); |
| for (int x = 0; x < selfChildrenSize; x++) { |
| XPathNode selfNode = xPathNode.getSelfChildren().get(x); |
| if (null != selfNode.getNodeValue()) { |
| selfNode.getNodeValue().endSelfNodeValue(this, selfRecords.get(x), attributes); |
| } |
| } |
| } |
| |
| CoreDescriptor xmlDescriptor = treeObjectBuilder.getDescriptor(); |
| |
| try { |
| // PROCESS COLLECTION MAPPINGS |
| //All populated containerValues need to be set on the object |
| if(null != populatedContainerValues){ |
| for (int populatedCVSize=populatedContainerValues.size(), i = populatedCVSize-1; i>=0; i--) { |
| ContainerValue cv = (populatedContainerValues.get(i)); |
| cv.setContainerInstance(currentObject, getContainerInstance(cv, cv.isDefaultEmptyContainer())); |
| } |
| } |
| |
| //Additionally if any containerValues are defaultEmptyContainerValues they need to be set to a new empty container |
| if(null != defaultEmptyContainerValues){ |
| for (int defaultEmptyCVSize=defaultEmptyContainerValues.size(),i = defaultEmptyCVSize-1; i>=0; i--) { |
| ContainerValue cv = (defaultEmptyContainerValues.get(i)); |
| cv.setContainerInstance(currentObject, getContainerInstance(cv, cv.isDefaultEmptyContainer())); |
| } |
| |
| } |
| // PROCESS NULL CAPABLE VALUES |
| // This must be done because the node may not have existed to |
| // trigger the mapping. |
| if(null != nullCapableValues) { |
| for (int x = 0, nullValuesSize = nullCapableValues.size(); x < nullValuesSize; x++) { |
| nullCapableValues.get(x).setNullValue(currentObject, session); |
| } |
| } |
| |
| // PROCESS TRANSFORMATION MAPPINGS |
| List<TransformationMapping> transformationMappings = treeObjectBuilder.getTransformationMappings(); |
| if (null != transformationMappings) { |
| for (int x = 0, transformationMappingsSize = transformationMappings.size(); x < transformationMappingsSize; x++) { |
| TransformationMapping transformationMapping = transformationMappings.get(x); |
| transformationMapping.readFromRowIntoObject((XMLRecord) transformationRecord, currentObject, session, true); |
| } |
| } |
| |
| Unmarshaller.Listener listener = unmarshaller.getUnmarshalListener(); |
| if (listener != null) { |
| if (this.parentRecord != null) { |
| listener.afterUnmarshal(currentObject, parentRecord.getCurrentObject()); |
| } else { |
| listener.afterUnmarshal(currentObject, null); |
| } |
| } |
| |
| // HANDLE POST BUILD EVENTS |
| if(xmlDescriptor.hasEventManager()) { |
| CoreDescriptorEventManager eventManager = xmlDescriptor.getEventManager(); |
| if (null != eventManager && eventManager.hasAnyEventListeners()) { |
| DescriptorEvent event = new DescriptorEvent(currentObject); |
| event.setSession((AbstractSession) session); |
| event.setRecord(null); //this); |
| event.setEventCode(DescriptorEventManager.PostBuildEvent); |
| eventManager.executeEvent(event); |
| } |
| } |
| } catch (EclipseLinkException e) { |
| if (null == xmlReader.getErrorHandler()) { |
| throw e; |
| } else { |
| SAXParseException saxParseException = new SAXParseException(null, getDocumentLocator(), e); |
| xmlReader.getErrorHandler().error(saxParseException); |
| } |
| } |
| |
| // if the object has any primary key fields set, add it to the cache |
| if(null != referenceResolver) { |
| if(null != xmlDescriptor) { |
| List primaryKeyFields = xmlDescriptor.getPrimaryKeyFields(); |
| if(null != primaryKeyFields) { |
| int primaryKeyFieldsSize = primaryKeyFields.size(); |
| if (primaryKeyFieldsSize > 0) { |
| CacheId pk = (CacheId) treeObjectBuilder.extractPrimaryKeyFromObject(currentObject, session); |
| for (int x=0; x<primaryKeyFieldsSize; x++) { |
| Object value = pk.getPrimaryKey()[x]; |
| if (null == value) { |
| Field pkField = (Field) xmlDescriptor.getPrimaryKeyFields().get(x); |
| pk.set(x, unmarshaller.getContext().getValueByXPath(currentObject, pkField.getXPath(), pkField.getNamespaceResolver(), Object.class)); |
| } |
| } |
| referenceResolver.putValue(xmlDescriptor.getJavaClass(), pk, currentObject); |
| |
| if (unmarshaller.getIDResolver() != null) { |
| try { |
| if (primaryKeyFieldsSize > 1) { |
| Map<String, Object> idWrapper = new HashMap<>(); |
| for (int x = 0; x < primaryKeyFieldsSize; x++) { |
| String idName = (String) xmlDescriptor.getPrimaryKeyFieldNames().get(x); |
| Object idValue = pk.getPrimaryKey()[x]; |
| idWrapper.put(idName, idValue); |
| } |
| unmarshaller.getIDResolver().bind(idWrapper, currentObject); |
| } else { |
| unmarshaller.getIDResolver().bind(pk.getPrimaryKey()[0], currentObject); |
| } |
| } catch (SAXException e) { |
| throw XMLMarshalException.unmarshalException(e); |
| } |
| } |
| |
| } |
| } |
| } |
| } |
| |
| if(null != parentRecord) { |
| reset(); |
| } |
| |
| // Set XML Location if applicable |
| if (xmlLocation != null && ((Descriptor) xmlDescriptor).getLocationAccessor() != null) { |
| ((Descriptor) xmlDescriptor).getLocationAccessor().setAttributeValueInObject(getCurrentObject(), xmlLocation); |
| } |
| } |
| |
| @Override |
| public void startPrefixMapping(String prefix, String uri) throws SAXException { |
| getUnmarshalNamespaceResolver().push(prefix, uri); |
| getPrefixesForFragment().put(prefix, uri); |
| } |
| |
| @Override |
| public void endPrefixMapping(String prefix) throws SAXException { |
| getUnmarshalNamespaceResolver().pop(prefix); |
| } |
| |
| @Override |
| public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException { |
| if (currentObject == null) { |
| initializeRecord(atts); |
| } |
| |
| XPathFragment xPathNodeXPathFragment = xPathNode.getXPathFragment(); |
| if((null != xPathNodeXPathFragment && xPathNodeXPathFragment.nameIsText()) || xpathNodeIsMixedContent) { |
| xpathNodeIsMixedContent = false; |
| NodeValue xPathNodeUnmarshalNodeValue = xPathNode.getUnmarshalNodeValue(); |
| if (null != xPathNodeUnmarshalNodeValue) { |
| boolean isIncludedInAttributeGroup = true; |
| if(xPathNodeUnmarshalNodeValue.isMappingNodeValue()) { |
| Mapping mapping = ((MappingNodeValue)xPathNodeUnmarshalNodeValue).getMapping(); |
| isIncludedInAttributeGroup = this.unmarshalAttributeGroup.containsAttributeInternal(mapping.getAttributeName()); |
| } |
| if(isIncludedInAttributeGroup) { |
| xPathNodeUnmarshalNodeValue.endElement(xPathFragment, this); |
| if (xPathNode.getParent() != null) { |
| xPathNode = xPathNode.getParent(); |
| } |
| } |
| } |
| } |
| |
| // set the root element's local name and namespace prefix and look for |
| // schema locations etc. |
| if (null == rootElementName && null == rootElementLocalName && parentRecord == null){ |
| rootElementLocalName = localName; |
| rootElementName = qName; |
| rootElementNamespaceUri = namespaceURI; |
| schemaLocation = atts.getValue(javax.xml.XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI, Constants.SCHEMA_LOCATION); |
| noNamespaceSchemaLocation = atts.getValue(javax.xml.XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI, Constants.NO_NS_SCHEMA_LOCATION); |
| } |
| |
| try { |
| if (null != selfRecords) { |
| for (int x = 0, selfRecordsSize = selfRecords.size(); x < selfRecordsSize; x++) { |
| UnmarshalRecord selfRecord = selfRecords.get(x); |
| if(selfRecord == null){ |
| getFragmentBuilder().startElement(namespaceURI, localName, qName, atts); |
| }else{ |
| selfRecord.startElement(namespaceURI, localName, qName, atts); |
| } |
| } |
| } |
| |
| if(unmappedLevel != -1 && unmappedLevel <= levelIndex) { |
| levelIndex++; |
| return; |
| } |
| |
| XPathNode node = null; |
| if (null != predictedNextXPathNode) { |
| |
| XPathFragment xpf = predictedNextXPathNode.getXPathFragment(); |
| |
| if (null != xpf && xPathNode == predictedNextXPathNode.getParent() && (localName == xpf.getLocalName() || localName.equals(xpf.getLocalName())) && (namespaceURI == xpf.getNamespaceURI() || namespaceURI.equals(xpf.getNamespaceURI())) && null == xpf.getPredicate() && !xpf.containsIndex()) { |
| |
| updateXPathFragment(qName, localName, namespaceURI); |
| node = predictedNextXPathNode; |
| } |
| } |
| |
| if (null == node) { |
| node = getNonAttributeXPathNode(namespaceURI, localName, qName, atts); |
| } |
| |
| if (null == node) { |
| NodeValue parentNodeValue = xPathNode.getUnmarshalNodeValue(); |
| if ((null == xPathNode.getXPathFragment()) && (parentNodeValue != null)) { |
| XPathFragment parentFragment = new XPathFragment(); |
| parentFragment.setNamespaceAware(isNamespaceAware()); |
| if(namespaceURI != null && namespaceURI.length() == 0){ |
| parentFragment.setLocalName(qName); |
| parentFragment.setNamespaceURI(null); |
| } else { |
| parentFragment.setLocalName(localName); |
| parentFragment.setNamespaceURI(namespaceURI); |
| } |
| if (parentNodeValue.startElement(parentFragment, this, atts)) { |
| levelIndex++; |
| } else { |
| // UNMAPPED CONTENT |
| startUnmappedElement(namespaceURI, localName, qName, atts); |
| return; |
| } |
| } else { |
| // UNMAPPED CONTENT |
| levelIndex++; |
| startUnmappedElement(namespaceURI, localName, qName, atts); |
| return; |
| } |
| } else { |
| |
| xPathNode = node; |
| unmarshalContext.startElement(this); |
| levelIndex++; |
| |
| if(xmlReader.getMediaType().isApplicationXML()) { |
| String xsiNilValue = atts.getValue(javax.xml.XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI, Constants.SCHEMA_NIL_ATTRIBUTE); |
| if (xsiNilValue != null) { |
| setNil(xsiNilValue.equals(Constants.BOOLEAN_STRING_TRUE) || xsiNilValue.equals("1")); |
| } |
| } |
| |
| if(node.getNullCapableValue() != null){ |
| getNullCapableValues().add(node.getNullCapableValue()); |
| } |
| |
| NodeValue nodeValue = node.getUnmarshalNodeValue(); |
| if (null != nodeValue) { |
| boolean isIncludedInAttributeGroup = true; |
| if(nodeValue.isMappingNodeValue()) { |
| Mapping mapping = ((MappingNodeValue)nodeValue).getMapping(); |
| isIncludedInAttributeGroup = this.unmarshalAttributeGroup.containsAttributeInternal(mapping.getAttributeName()); |
| } |
| if (!isIncludedInAttributeGroup || !nodeValue.startElement(xPathFragment, this, atts)) { |
| // UNMAPPED CONTENT |
| startUnmappedElement(namespaceURI, localName, qName, atts); |
| return; |
| } |
| } |
| |
| //Handle Attributes |
| if(xPathNode.getAttributeChildren() != null || xPathNode.getAnyAttributeNodeValue() != null || selfRecords != null) { |
| for (int i = 0, size=atts.getLength(); i < size; i++) { |
| String attNamespace = atts.getURI(i); |
| String attLocalName = atts.getLocalName(i); |
| String value = atts.getValue(i); |
| NodeValue attributeNodeValue = null; |
| |
| // Some parsers don't set the URI/local name for namespace |
| // attributes |
| if ((attLocalName == null) || (attLocalName.length() == 0)) { |
| String qname = atts.getQName(i); |
| if (qname != null) { |
| int qnameLength = qname.length(); |
| if (qnameLength > 0) { |
| int idx = qname.indexOf(Constants.COLON); |
| if (idx > 0) { |
| attLocalName = qname.substring(idx + 1, qnameLength); |
| String attPrefix = qname.substring(0, idx); |
| if (attPrefix.equals(javax.xml.XMLConstants.XMLNS_ATTRIBUTE)) { |
| attNamespace = javax.xml.XMLConstants.XMLNS_ATTRIBUTE_NS_URI; |
| } |
| } else { |
| attLocalName = qname; |
| if (attLocalName.equals(javax.xml.XMLConstants.XMLNS_ATTRIBUTE)) { |
| attNamespace = javax.xml.XMLConstants.XMLNS_ATTRIBUTE_NS_URI; |
| } |
| } |
| } |
| } |
| } |
| |
| //Look for any Self-Mapping nodes that may want this attribute. |
| if (this.selfRecords != null) { |
| for (int j = 0; j < selfRecords.size(); j++) { |
| UnmarshalRecord nestedRecord = selfRecords.get(j); |
| if(nestedRecord != null){ |
| attributeNodeValue = nestedRecord.getAttributeChildNodeValue(attNamespace, attLocalName); |
| if (attributeNodeValue != null) { |
| attributeNodeValue.attribute(nestedRecord, attNamespace, attLocalName, value); |
| } |
| } |
| } |
| } |
| if (attributeNodeValue == null) { |
| attributeNodeValue = this.getAttributeChildNodeValue(attNamespace, attLocalName); |
| |
| try { |
| if (attributeNodeValue != null) { |
| if(attributeNodeValue.isMappingNodeValue()) { |
| Mapping mapping = ((MappingNodeValue)attributeNodeValue).getMapping(); |
| if(!unmarshalAttributeGroup.containsAttributeInternal(mapping.getAttributeName())) { |
| continue; |
| } |
| } |
| attributeNodeValue.attribute(this, attNamespace, attLocalName, value); |
| } else { |
| if (xPathNode.getAnyAttributeNodeValue() != null) { |
| xPathNode.getAnyAttributeNodeValue().attribute(this, attNamespace, attLocalName, value); |
| } |
| } |
| } catch(EclipseLinkException e) { |
| if ((null == xmlReader) || (null == xmlReader.getErrorHandler())) { |
| throw e; |
| } else { |
| SAXParseException saxParseException = new SAXParseException(e.getLocalizedMessage(), getDocumentLocator(), e); |
| xmlReader.getErrorHandler().warning(saxParseException); |
| } |
| } |
| } |
| } |
| } |
| } |
| if(prefixesForFragment != null){ |
| this.prefixesForFragment.clear(); |
| } |
| } catch (EclipseLinkException e) { |
| if ((null == xmlReader) || (null == xmlReader.getErrorHandler())) { |
| throw e; |
| } else { |
| SAXParseException saxParseException = new SAXParseException(e.getLocalizedMessage(), getDocumentLocator(), e); |
| xmlReader.getErrorHandler().error(saxParseException); |
| } |
| } |
| } |
| |
| private void updateXPathFragment(String qName, String localName, String namespaceURI) { |
| if (namespaceURI != null && namespaceURI.length() == 0) { |
| xPathFragment.setLocalName(qName); |
| xPathFragment.setNamespaceURI(null); |
| } else { |
| xPathFragment.setLocalName(localName); |
| xPathFragment.setNamespaceURI(namespaceURI); |
| } |
| } |
| |
| public void startUnmappedElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException { |
| if(xmlReader.getMediaType().isApplicationXML() && null == selfRecords && !isSelfRecord) { |
| ErrorHandler errorHandler = xmlReader.getErrorHandler(); |
| // Bug 452584 - check if a warning exception should be generated when an unmapped element is encountered |
| if(null != errorHandler && unmarshaller.shouldWarnOnUnmappedElement()) { |
| StringBuilder messageBuilder = new StringBuilder("unexpected element (uri:\""); |
| if(null != namespaceURI) { |
| messageBuilder.append(namespaceURI); |
| } |
| messageBuilder.append("\", local:\""); |
| messageBuilder.append(localName); |
| messageBuilder.append("\"). Expected elements are "); |
| List<XPathNode> nonAttributeChildren = xPathNode.getNonAttributeChildren(); |
| if(nonAttributeChildren == null || nonAttributeChildren.size() == 0) { |
| messageBuilder.append("(none)"); |
| } else { |
| for(int x=0, size=nonAttributeChildren.size(); x<size; x++) { |
| XPathFragment nonAttributeChildXPathFragment = nonAttributeChildren.get(x).getXPathFragment(); |
| messageBuilder.append("<{"); |
| String nonAttributeChildXPathFragmentNamespaceURI = nonAttributeChildXPathFragment.getNamespaceURI(); |
| if(null != nonAttributeChildXPathFragmentNamespaceURI) { |
| messageBuilder.append(nonAttributeChildXPathFragmentNamespaceURI); |
| } |
| messageBuilder.append('}'); |
| messageBuilder.append(nonAttributeChildXPathFragment.getLocalName()); |
| messageBuilder.append('>'); |
| if(x<size-1) { |
| messageBuilder.append(','); |
| } |
| } |
| } |
| errorHandler.warning(new SAXParseException(messageBuilder.toString(), getDocumentLocator())); |
| } |
| } |
| if ((null != selfRecords) || (null == xmlReader) || isSelfRecord()) { |
| if(-1 == unmappedLevel) { |
| this.unmappedLevel = this.levelIndex; |
| } |
| return; |
| } |
| Class unmappedContentHandlerClass = unmarshaller.getUnmappedContentHandlerClass(); |
| UnmappedContentHandler unmappedContentHandler; |
| if (null == unmappedContentHandlerClass) { |
| unmappedContentHandler = DEFAULT_UNMAPPED_CONTENT_HANDLER; |
| } else { |
| 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(this, unmappedContentHandler); |
| unmappedContentHandlerWrapper.startElement(namespaceURI, localName, qName, atts); |
| xmlReader.setContentHandler(unmappedContentHandlerWrapper); |
| xmlReader.setLexicalHandler(unmappedContentHandlerWrapper); |
| } |
| |
| @Override |
| public void endElement(String namespaceURI, String localName, String qName) throws SAXException { |
| try { |
| if (null != selfRecords) { |
| for (int x = 0, selfRecordsSize = selfRecords.size(); x < selfRecordsSize; x++) { |
| UnmarshalRecord selfRecord = selfRecords.get(x); |
| if(selfRecord != null){ |
| selfRecord.endElement(namespaceURI, localName, qName); |
| }else{ |
| getFragmentBuilder().endSelfElement(namespaceURI, localName, qName); |
| } |
| } |
| } |
| if(-1 != unmappedLevel && unmappedLevel <= levelIndex) { |
| if(levelIndex == unmappedLevel) { |
| unmappedLevel = -1; |
| } |
| levelIndex--; |
| return; |
| } |
| NodeValue unmarshalNodeValue = xPathNode.getUnmarshalNodeValue(); |
| if (null != unmarshalNodeValue) { |
| boolean isIncludedInAttributeGroup = true; |
| if(unmarshalNodeValue.isMappingNodeValue()) { |
| Mapping mapping = ((MappingNodeValue)unmarshalNodeValue).getMapping(); |
| isIncludedInAttributeGroup = this.unmarshalAttributeGroup.containsAttributeInternal(mapping.getAttributeName()); |
| } |
| try { |
| if(isIncludedInAttributeGroup) { |
| unmarshalNodeValue.endElement(xPathFragment, this); |
| } else { |
| resetStringBuffer(); |
| } |
| |
| } catch(EclipseLinkException e) { |
| if ((null == xmlReader) || (null == xmlReader.getErrorHandler())) { |
| throw e; |
| } else { |
| SAXParseException saxParseException = new SAXParseException(e.getLocalizedMessage(), getDocumentLocator(), e); |
| xmlReader.getErrorHandler().warning(saxParseException); |
| } |
| } |
| } else { |
| XPathNode textNode = xPathNode.getTextNode(); |
| |
| if (null != textNode && getStringBuffer().length() == 0) { |
| NodeValue textNodeUnmarshalNodeValue = textNode.getUnmarshalNodeValue(); |
| if(textNode.isWhitespaceAware()){ |
| if (textNodeUnmarshalNodeValue.isMappingNodeValue()) { |
| Mapping mapping = ((MappingNodeValue)textNodeUnmarshalNodeValue).getMapping(); |
| if(mapping.isAbstractDirectMapping() && isNil()) { |
| Object nullValue = ((DirectMapping)mapping).getNullValue(); |
| if(!(Constants.EMPTY_STRING.equals(nullValue))) { |
| setAttributeValue(null, mapping); |
| this.removeNullCapableValue((NullCapableValue)textNodeUnmarshalNodeValue); |
| } |
| } else { |
| textNodeUnmarshalNodeValue.endElement(xPathFragment, this); |
| } |
| setNil(false); |
| } |
| }else{ |
| |
| //This means empty tag |
| if(textNodeUnmarshalNodeValue.isMappingNodeValue()) { |
| Mapping mapping = ((MappingNodeValue)textNodeUnmarshalNodeValue).getMapping(); |
| if(mapping.isAbstractDirectMapping() && isNil()) { |
| Object nullValue = ((DirectMapping) mapping).getNullValue(); |
| if (!(Constants.EMPTY_STRING.equals(nullValue))) { |
| setAttributeValue(null, mapping); |
| } |
| } |
| if(mapping.isAbstractDirectMapping() && !isNil() && ((DirectMapping)mapping).getNullPolicy().isNullRepresentedByXsiNil()){ |
| removeNullCapableValue((NullCapableValue)textNodeUnmarshalNodeValue); } |
| } |
| |
| } |
| } |
| } |
| XPathFragment xPathFragment = xPathNode.getXPathFragment(); |
| if((null != xPathFragment && xPathFragment.nameIsText()) || (xpathNodeIsMixedContent && xPathNode.getParent() != null)) { |
| xPathNode = xPathNode.getParent(); |
| } |
| |
| NodeValue xPathNodeUnmarshalNodeValue = xPathNode.getUnmarshalNodeValue(); |
| if (null != xPathNodeUnmarshalNodeValue && xPathNodeUnmarshalNodeValue.isContainerValue()) { |
| predictedNextXPathNode = xPathNode; |
| } else { |
| predictedNextXPathNode = xPathNode.getNextNode(); |
| } |
| |
| if (null != xPathNode.getParent()) { |
| xPathNode = xPathNode.getParent(); |
| } |
| |
| xpathNodeIsMixedContent = false; |
| unmarshalContext.endElement(this); |
| |
| typeQName = null; |
| levelIndex--; |
| if(isNil() && levelIndex > 0) { |
| setNil(false); |
| } |
| if ((0 == levelIndex) && (null !=parentRecord) && !isSelfRecord()) { |
| endDocument(); |
| // don't endElement on, or pass control to, a 'self' parent |
| UnmarshalRecord pRec = parentRecord; |
| while (pRec.isSelfRecord()) { |
| pRec = pRec.getParentRecord(); |
| } |
| pRec.endElement(namespaceURI, localName, qName); |
| xmlReader.setContentHandler(pRec); |
| xmlReader.setLexicalHandler(pRec); |
| } |
| |
| } catch (EclipseLinkException e) { |
| if ((null == xmlReader) || (null == xmlReader.getErrorHandler())) { |
| throw e; |
| } else { |
| Locator locator = xmlReader.getLocator(); |
| SAXParseException saxParseException = new SAXParseException(null, getDocumentLocator(), e); |
| xmlReader.getErrorHandler().warning(saxParseException); |
| } |
| } |
| } |
| |
| @Override |
| public void endUnmappedElement(String namespaceURI, String localName, String qName) throws SAXException { |
| typeQName = null; |
| levelIndex--; |
| if ((0 == levelIndex) && (null != parentRecord) && !isSelfRecord()) { |
| endDocument(); |
| // don't endElement on, or pass control to, a 'self' parent |
| UnmarshalRecord pRec = parentRecord; |
| while (pRec.isSelfRecord()) { |
| pRec = pRec.getParentRecord(); |
| } |
| pRec.endElement(namespaceURI, localName, qName); |
| xmlReader.setContentHandler(pRec); |
| xmlReader.setLexicalHandler(pRec); |
| } |
| setNil(false); // null unmapped element processed. We have to reset nil status |
| } |
| |
| @Override |
| public void characters(char[] ch, int start, int length) throws SAXException { |
| if(currentObject == null){ |
| return; |
| } |
| try { |
| int strBufferInitialLength = -1; |
| if (null != selfRecords) { |
| strBufferInitialLength = getStringBuffer().length(); |
| for (int x = 0, selfRecordsSize = selfRecords.size(); x < selfRecordsSize; x++) { |
| UnmarshalRecord selfRecord = selfRecords.get(x); |
| if(selfRecord != null){ |
| selfRecord.characters(ch, start, length); |
| } else { |
| getFragmentBuilder().characters(ch, start, length); |
| } |
| } |
| } |
| if(-1 != unmappedLevel && unmappedLevel <= levelIndex) { |
| return; |
| } |
| XPathNode textNode = xPathNode.getTextNode(); |
| if (null == textNode) { |
| textNode = xPathNode.getAnyNode(); |
| if (textNode != null) { |
| xpathNodeIsMixedContent = true; |
| this.xPathFragment.setLocalName(null); |
| this.xPathFragment.setNamespaceURI(null); |
| if (0 == length) { |
| return; |
| } |
| } |
| } |
| if (null != textNode) { |
| if(textNode.getUnmarshalNodeValue().isMixedContentNodeValue()) { |
| String tmpString = new String(ch, start, length); |
| if (!textNode.isWhitespaceAware() && tmpString.trim().length() == 0) { |
| return; |
| } |
| } |
| xPathNode = textNode; |
| unmarshalContext.characters(this); |
| } |
| |
| NodeValue unmarshalNodeValue = xPathNode.getUnmarshalNodeValue(); |
| if (null != unmarshalNodeValue && !unmarshalNodeValue.isWrapperNodeValue()) { |
| if(strBufferInitialLength == -1) { |
| getStringBuffer().append(ch, start, length); |
| } else { |
| StrBuffer strBuffer = getStringBuffer(); |
| if(strBufferInitialLength == strBuffer.length()) { |
| strBuffer.append(ch, start, length); |
| } |
| } |
| } |
| } catch (EclipseLinkException e) { |
| if (null == xmlReader.getErrorHandler()) { |
| throw e; |
| } else { |
| SAXParseException saxParseException = new SAXParseException(null, getDocumentLocator(), e); |
| xmlReader.getErrorHandler().error(saxParseException); |
| } |
| } |
| } |
| |
| @Override |
| public void characters(CharSequence characters) throws SAXException { |
| if(null != characters) { |
| String string = characters.toString(); |
| characters(string.toCharArray(), 0, string.length()); |
| } |
| } |
| |
| @Override |
| public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException { |
| } |
| |
| @Override |
| public void processingInstruction(String target, String data) throws SAXException { |
| } |
| |
| @Override |
| public void skippedEntity(String name) throws SAXException { |
| } |
| |
| /** |
| * INTERNAL: |
| */ |
| @Override |
| public XPathNode getNonAttributeXPathNode(String namespaceURI, String localName, String qName, Attributes attributes) { |
| if (0 == levelIndex) { |
| return xPathNode; |
| } |
| updateXPathFragment(qName, localName, namespaceURI); |
| |
| Map<XPathFragment, XPathNode> nonAttributeChildrenMap = xPathNode.getNonAttributeChildrenMap(); |
| |
| if (null != nonAttributeChildrenMap) { |
| XPathNode resultNode; |
| |
| if (unmarshaller.isCaseInsensitive()){ |
| resultNode = getNodeFromLookupTable(nonAttributeChildrenMap, false); |
| } else { |
| resultNode = nonAttributeChildrenMap.get(xPathFragment); |
| } |
| |
| XPathNode nonPredicateNode = null; |
| if(resultNode != null && resultNode.hasPredicateSiblings()) { |
| nonPredicateNode = resultNode; |
| resultNode = null; |
| } |
| if (null == resultNode) { |
| // POSITIONAL MAPPING |
| int newIndex; |
| if (null == this.indexMap) { |
| this.indexMap = new HashMap(); |
| newIndex = 1; |
| } else { |
| Integer oldIndex = indexMap.get(xPathFragment); |
| if (null == oldIndex) { |
| newIndex = 1; |
| } else { |
| newIndex = oldIndex.intValue() + 1; |
| } |
| } |
| indexMap.put(xPathFragment, newIndex); |
| XPathFragment predicateFragment = new XPathFragment(); |
| predicateFragment.setNamespaceAware(isNamespaceAware()); |
| predicateFragment.setNamespaceURI(xPathFragment.getNamespaceURI()); |
| predicateFragment.setLocalName(xPathFragment.getLocalName()); |
| predicateFragment.setIndexValue(newIndex); |
| resultNode = nonAttributeChildrenMap.get(predicateFragment); |
| if (null == resultNode) { |
| predicateFragment.setIndexValue(-1); |
| if(attributes != null){ |
| for(int x = 0, length = attributes.getLength(); x<length; x++) { |
| XPathFragment conditionFragment = new XPathFragment(); |
| conditionFragment.setLocalName(attributes.getLocalName(x)); |
| conditionFragment.setNamespaceURI(attributes.getURI(x)); |
| conditionFragment.setAttribute(true); |
| XPathPredicate condition = new XPathPredicate(conditionFragment, attributes.getValue(x)); |
| predicateFragment.setPredicate(condition); |
| resultNode = nonAttributeChildrenMap.get(predicateFragment); |
| if(null != resultNode) { |
| break; |
| } |
| } |
| } |
| //if json, check for text wrapper before handing off to the any |
| if(null == resultNode && xPathNode.getTextNode() != null){ |
| XPathFragment textWrapperFragment = getTextWrapperFragment(); |
| if(textWrapperFragment != null && localName.equals(textWrapperFragment.getLocalName())){ |
| resultNode = xPathNode.getTextNode(); |
| } |
| } |
| if(null == resultNode && null == nonPredicateNode) { |
| // ANY MAPPING |
| resultNode = xPathNode.getAnyNode(); |
| } |
| } |
| } |
| if(resultNode == null && nonPredicateNode != null) { |
| return nonPredicateNode; |
| } |
| return resultNode; |
| } |
| return null; |
| } |
| |
| @Override |
| public String resolveNamespacePrefix(String prefix) { |
| String namespaceURI = getUnmarshalNamespaceResolver().getNamespaceURI(prefix); |
| if(null == namespaceURI && null != parentRecord) { |
| namespaceURI = parentRecord.resolveNamespacePrefix(prefix); |
| } |
| return namespaceURI; |
| } |
| |
| @Override |
| public String resolveNamespaceUri(String uri) { |
| String prefix = getUnmarshalNamespaceResolver().getPrefix(uri); |
| if (null == prefix) { |
| if (null != parentRecord) { |
| prefix = parentRecord.resolveNamespaceUri(uri); |
| } |
| } |
| return prefix; |
| } |
| |
| public NodeValue getSelfNodeValueForAttribute(String namespace, String localName) { |
| if (this.selfRecords != null) { |
| for (int i = 0, selfRecordsSize = selfRecords.size(); i < selfRecordsSize; i++) { |
| UnmarshalRecord nestedRecord = selfRecords.get(i); |
| if(nestedRecord != null){ |
| NodeValue node = nestedRecord.getAttributeChildNodeValue(namespace, localName); |
| if (node != null) { |
| return node; |
| } |
| } |
| } |
| } |
| return null; |
| } |
| |
| @Override |
| public NodeValue getAttributeChildNodeValue(String namespace, String localName) { |
| Map<XPathFragment, XPathNode> attributeChildrenMap = xPathNode.getAttributeChildrenMap(); |
| if (attributeChildrenMap != null) { |
| XPathNode resultNode; |
| xPathFragment.setLocalName(localName); |
| xPathFragment.setNamespaceURI(namespace); |
| |
| if (unmarshaller.isCaseInsensitive()){ |
| resultNode = getNodeFromLookupTable(attributeChildrenMap, true); |
| } else { |
| resultNode = attributeChildrenMap.get(xPathFragment); |
| } |
| |
| if (resultNode != null) { |
| return resultNode.getUnmarshalNodeValue(); |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * INTERNAL: |
| * Retrieves the XPathNode by searching in the auxiliary case insensitive lookup table. |
| * |
| * @param childrenMap Original Map for construction of the auxiliary table. |
| * @param isAttribute Determine if searching for an element or an attribute. |
| * @return XPathNode object reference, which is also present in the original children map. |
| * @since 2.6.0 |
| */ |
| private XPathNode getNodeFromLookupTable(Map<XPathFragment, XPathNode> childrenMap, boolean isAttribute) { |
| Map<String, XPathNode> lookupTable = xPathNode.getChildrenLookupTable(isAttribute); |
| |
| if(!xPathNode.isChildrenLookupTableFilled(isAttribute)){ |
| this.fillLookupTable(childrenMap, lookupTable); |
| xPathNode.setChildrenLookupTableFilled(isAttribute); |
| } |
| |
| String lowerCaseFragment = xPathFragment.getLocalName().toLowerCase(); |
| if (!xPathFragment.getChildrenCollisionSet(isAttribute).add(lowerCaseFragment)) |
| handleCollision(lowerCaseFragment, false); |
| return lookupTable.get(lowerCaseFragment); |
| } |
| |
| /** |
| * INTERNAL: |
| * Creates an auxiliary lookup table containing lower-cased localNames of XPathFragments. |
| * |
| * Does NOT pass the Turkey test. |
| * |
| * For future development: Handle ISO-8859-9 encoding. |
| * if (encoding.equals("ISO-8859-9")) { |
| * String auxLocalName = entry.getKey().getLocalName().toLowerCase(Locale.forLanguageTag("tr-TR")); |
| * } |
| * |
| * @param childrenMap Table from which the data is acquired. |
| * @param lookupTable Table to which the lower-cased data is stored. |
| * @since 2.6.0 |
| */ |
| private void fillLookupTable(Map<XPathFragment, XPathNode> childrenMap, Map<String, XPathNode> lookupTable) { |
| String lookupName; |
| for (Map.Entry<XPathFragment, XPathNode> entry : childrenMap.entrySet()) { |
| lookupName = entry.getKey().getLocalName().toLowerCase(); |
| if (lookupTable.put(lookupName, entry.getValue()) != null){ |
| handleCollision(lookupName, true); |
| } |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * Handles collisions, i.e. fragments or fields with the same name, different case. |
| * |
| * @param lookupName Lookup variant of the localName. |
| * @param onXPathNode true - the collision occurred on XPathNode (case for Java fields), |
| * false - the collision occurred on XPathFragment (case for XML elements/attributes). |
| * @since 2.6.0 |
| */ |
| private void handleCollision(String lookupName, boolean onXPathNode) { |
| StringBuilder sb = new StringBuilder() |
| .append(">\nUnmarshalRecordImpl.handleCollision() -->\tCOLLISION on ") |
| .append( |
| onXPathNode |
| ? "XPathNode fields by case insensitive localName \"" |
| : "XPathFragments by case insensitive localName \"" |
| ).append(lookupName).append("\"."); |
| |
| // session.setLogLevel(SessionLog.WARNING); // for debugging |
| ((AbstractSession) session).logMessage(CommandProcessor.LOG_WARNING, sb.toString()); |
| } |
| |
| @Override |
| public SAXFragmentBuilder getFragmentBuilder() { |
| if(this.fragmentBuilder == null){ |
| fragmentBuilder = new SAXFragmentBuilder(this); |
| } |
| return fragmentBuilder; |
| } |
| |
| @Override |
| public void setFragmentBuilder(SAXFragmentBuilder builder) { |
| this.fragmentBuilder = builder; |
| } |
| |
| @Override |
| public void resetStringBuffer() { |
| this.getStringBuffer().reset(); |
| this.isBufferCDATA = false; |
| } |
| |
| @Override |
| public boolean isBufferCDATA() { |
| return isBufferCDATA; |
| } |
| |
| @Override |
| public void comment(char[] data, int start, int length) { |
| } |
| |
| @Override |
| public void startCDATA() { |
| if (null != xPathNode && xPathNode.getUnmarshalNodeValue() != null) { |
| this.isBufferCDATA = true; |
| } |
| } |
| |
| @Override |
| public void endCDATA() { |
| } |
| |
| @Override |
| public void startEntity(String entity) { |
| } |
| |
| @Override |
| public void endEntity(String entity) { |
| } |
| |
| @Override |
| public void startDTD(String a, String b, String c) { |
| } |
| |
| @Override |
| public void endDTD() { |
| } |
| |
| /** |
| * Sets the flag which indicates if this UnmarshalRecord |
| * represents a 'self' record |
| * |
| * @param isSelfRecord true if this record represents |
| * 'self', false otherwise |
| */ |
| @Override |
| public void setSelfRecord(boolean isSelfRecord) { |
| this.isSelfRecord = isSelfRecord; |
| } |
| |
| /** |
| * Indicates if this UnmarshalRecord represents a 'self' record |
| * |
| * @return true if this record represents 'self', false otherwise |
| */ |
| @Override |
| public boolean isSelfRecord() { |
| return isSelfRecord; |
| } |
| |
| @Override |
| public int getLevelIndex() { |
| return levelIndex; |
| } |
| |
| /** |
| * INTERNAL |
| * @since EclipseLink 2.5.0 |
| */ |
| @Override |
| public void setAttributeValue(Object value, Mapping mapping) { |
| this.unmarshalContext.setAttributeValue(this, value, mapping); |
| } |
| |
| @Override |
| public void addAttributeValue(ContainerValue containerValue, Object value) { |
| this.unmarshalContext.addAttributeValue(this, containerValue, value); |
| } |
| |
| @Override |
| public void addAttributeValue(ContainerValue containerValue, Object value, Object collection) { |
| this.unmarshalContext.addAttributeValue(this, containerValue, value, collection); |
| } |
| |
| @Override |
| public void setAttributeValueNull(ContainerValue containerValue) { |
| this.unmarshalContext.setAttributeValue(this, null, containerValue.getMapping()); |
| int containerIndex = containerValue.getIndex(); |
| populatedContainerValues.remove(containerValue); |
| containerInstances[containerIndex] = null; |
| } |
| |
| @Override |
| public void reference(Reference reference) { |
| this.unmarshalContext.reference(reference); |
| } |
| |
| @Override |
| public void unmappedContent() { |
| if(this.xPathNode.getParent() != null) { |
| xPathNode = xPathNode.getParent(); |
| } |
| this.unmarshalContext.unmappedContent(this); |
| } |
| |
| @Override |
| public UnmarshalRecord getChildUnmarshalRecord(ObjectBuilder treeObjectBuilder) { |
| if(childRecord != null && !childRecord.isSelfRecord()){ |
| childRecord.initialize(treeObjectBuilder); |
| childRecord.setParentRecord(this); |
| return childRecord; |
| }else{ |
| childRecord = new UnmarshalRecordImpl(treeObjectBuilder, referenceResolver); |
| childRecord.setSession(session); |
| childRecord.setUnmarshaller(unmarshaller); |
| childRecord.setTextWrapperFragment(textWrapperFragment); |
| childRecord.setXMLReader(this.xmlReader); |
| childRecord.setFragmentBuilder(fragmentBuilder); |
| childRecord.setUnmarshalNamespaceResolver(unmarshalNamespaceResolver); |
| childRecord.setParentRecord(this); |
| } |
| return childRecord; |
| } |
| |
| /** |
| * INTERNAL: |
| */ |
| @Override |
| public void setUnmarshaller(Unmarshaller unmarshaller) { |
| this.unmarshaller = unmarshaller; //super.setUnmarshaller(unmarshaller); |
| if(xPathFragment != null){ |
| xPathFragment.setNamespaceAware(isNamespaceAware()); |
| } |
| } |
| |
| /** |
| * INTERNAL |
| * Returns a Map of any prefix mappings that were made before the most recent start |
| * element event. This Map is used so the prefix mappings can be passed along to a |
| * fragment builder in the event that the element in question is going to be unmarshalled |
| * as a Node. |
| */ |
| @Override |
| public Map<String, String> getPrefixesForFragment() { |
| if(prefixesForFragment == null){ |
| prefixesForFragment = new HashMap<>(); |
| } |
| |
| return prefixesForFragment; |
| } |
| |
| |
| @Override |
| public char getNamespaceSeparator(){ |
| return xmlReader.getNamespaceSeparator(); |
| } |
| |
| @Override |
| public void setTextWrapperFragment(XPathFragment newTextWrapperFragment) { |
| textWrapperFragment = newTextWrapperFragment; |
| } |
| |
| @Override |
| public XPathFragment getTextWrapperFragment() { |
| if(xmlReader.getMediaType() .isApplicationJSON()){ |
| if(textWrapperFragment == null){ |
| textWrapperFragment = new XPathFragment(); |
| textWrapperFragment.setLocalName(unmarshaller.getValueWrapper()); |
| textWrapperFragment.setNamespaceAware(isNamespaceAware()); |
| textWrapperFragment.setNamespaceSeparator(getNamespaceSeparator()); |
| } |
| return textWrapperFragment; |
| } |
| return null; |
| } |
| |
| /** |
| * INTERNAL: |
| * If the UnmarshalRecord has a ReferenceResolver, tell it to resolve its |
| * references. |
| * @since EclipseLink 2.5.0 |
| */ |
| @Override |
| public void resolveReferences(CoreAbstractSession abstractSession, IDResolver idResolver) { |
| if(null != referenceResolver) { |
| referenceResolver.resolveReferences(abstractSession, idResolver, unmarshaller.getErrorHandler()); |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * @since EclipseLink 2.5.0 |
| */ |
| @Override |
| public Root createRoot() { |
| return unmarshaller.createRoot(); |
| } |
| |
| /** |
| * WHAT ABOUT THESE? |
| */ |
| |
| private Field convertToXMLField(CoreField key) { |
| return (Field) key; |
| } |
| |
| @Override |
| public CoreAbstractSession getSession() { |
| return session; |
| } |
| |
| @Override |
| public Unmarshaller getUnmarshaller() { |
| return unmarshaller; |
| } |
| |
| @Override |
| public boolean isNamespaceAware() { |
| return namespaceAware; |
| } |
| |
| @Override |
| public Object getCurrentObject() { |
| return currentObject; |
| } |
| |
| @Override |
| public XPathQName getLeafElementType() { |
| return leafElementType; |
| } |
| |
| @Override |
| public void setCurrentObject(Object object) { |
| this.currentObject = object; |
| } |
| |
| @Override |
| public void setLeafElementType(QName type) { |
| if (type != null) { |
| setLeafElementType(new XPathQName(type, isNamespaceAware())); |
| } else { |
| setLeafElementType((XPathQName) null); |
| } |
| } |
| |
| public void setLeafElementType(XPathQName type) { |
| leafElementType = type; |
| } |
| |
| @Override |
| public void setSession(CoreAbstractSession session) { |
| this.session = session; |
| } |
| |
| @Override |
| public CoreAttributeGroup getUnmarshalAttributeGroup() { |
| return unmarshalAttributeGroup; |
| } |
| |
| @Override |
| public void setUnmarshalAttributeGroup(CoreAttributeGroup unmarshalAttributeGroup) { |
| this.unmarshalAttributeGroup = unmarshalAttributeGroup; |
| } |
| |
| /** |
| * @since EclipseLink 2.6.0 |
| */ |
| @Override |
| public ConversionManager getConversionManager() { |
| if(null == conversionManager) { |
| conversionManager = (ConversionManager) session.getDatasourcePlatform().getConversionManager(); |
| } |
| return conversionManager; |
| } |
| |
| } |