com.sun.jersey.json.impl.MoxyXmlStructure Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ehcache Show documentation
Show all versions of ehcache Show documentation
Ehcache is an open source, standards-based cache used to boost performance,
offload the database and simplify scalability. Ehcache is robust, proven and full-featured and
this has made it the most widely-used Java-based cache.
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 2012 Oracle and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can
* obtain a copy of the License at
* http://glassfish.java.net/public/CDDL+GPL_1_1.html
* or packager/legal/LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at packager/legal/LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*
* Contributor(s):
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
package com.sun.jersey.json.impl;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Stack;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.namespace.QName;
import org.eclipse.persistence.descriptors.ClassDescriptor;
import org.eclipse.persistence.internal.oxm.MappingNodeValue;
import org.eclipse.persistence.internal.oxm.TreeObjectBuilder;
import org.eclipse.persistence.internal.oxm.XPathFragment;
import org.eclipse.persistence.internal.oxm.XPathNode;
import org.eclipse.persistence.jaxb.JAXBHelper;
import org.eclipse.persistence.mappings.DatabaseMapping;
import org.eclipse.persistence.mappings.converters.Converter;
import org.eclipse.persistence.mappings.converters.TypeConversionConverter;
import org.eclipse.persistence.mappings.foundation.AbstractCompositeDirectCollectionMapping;
import org.eclipse.persistence.oxm.XMLContext;
import org.eclipse.persistence.oxm.XMLDescriptor;
import org.eclipse.persistence.oxm.mappings.XMLCompositeCollectionMapping;
import org.eclipse.persistence.sessions.DatabaseSession;
/**
* Implementation of {@code JaxbXmlDocumentStructure} for MOXy JAXB provider.
*
* @author Michal Gajdos (michal.gajdos at oracle.com)
*/
public class MoxyXmlStructure extends DefaultJaxbXmlDocumentStructure {
private final class EntityType {
private final Type type;
private EntityType(Type type) {
this.type = type;
}
}
/**
* For caching purposes and because of the fact that there is no easy way to go back to the parent node ({@code getParent}
* returns {@code null}).
*/
private final class XPathNodeWrapper {
private Map elementTypeMap = new HashMap();
private Map attributeTypeMap = new HashMap();
private Map qNamesOfExpElems = new HashMap();
private Map qNamesOfExpAttrs = new HashMap();
private final XPathNode xPathNode;
private final XPathNodeWrapper parent;
private final ClassDescriptor classDescriptor;
private final QName name;
private final MappingNodeValue nodeValue;
public XPathNodeWrapper currentType;
public XPathNodeWrapper(final QName name) {
this(null, null, null, null, name);
}
public XPathNodeWrapper(final XPathNode xPathNode,
final XPathNodeWrapper parent,
final MappingNodeValue nodeValue,
final ClassDescriptor classDescriptor,
final QName name) {
this.xPathNode = xPathNode;
this.parent = parent;
this.nodeValue = nodeValue;
this.classDescriptor = classDescriptor;
this.name = name;
}
public Map getExpectedElementsMap() {
if (qNamesOfExpElems.isEmpty()) {
qNamesOfExpElems = qnameCollectionToMap(getExpectedElements(), true);
}
return qNamesOfExpElems;
}
public Map getExpectedAttributesMap() {
if (qNamesOfExpElems.isEmpty()) {
qNamesOfExpAttrs = qnameCollectionToMap(getExpectedAttributes(), false);
}
return qNamesOfExpAttrs;
}
public Map getEntitiesTypesMap(boolean isAttribute) {
Map entitiesTypes = isAttribute ? attributeTypeMap : elementTypeMap;
if (entitiesTypes.isEmpty()) {
final Map nodeMap =
isAttribute ? xPathNode.getAttributeChildrenMap() : xPathNode.getNonAttributeChildrenMap();
if (nodeMap != null) {
for(Map.Entry entry : nodeMap.entrySet()) {
entitiesTypes.put(entry.getKey().getLocalName(), new EntityType(entry.getKey().getXMLField().getType()));
}
}
}
return entitiesTypes;
}
public MappingNodeValue getNodeValue() {
return nodeValue;
}
public ClassDescriptor getClassDescriptor() {
return classDescriptor;
}
public boolean isInSameArrayAs(final XPathNodeWrapper wrapper) {
return wrapper != null
&& this.classDescriptor == wrapper.classDescriptor && this.parent == wrapper.parent;
}
}
/**
* Stack of nodes which processing has started but is not finished yet.
*
* @see XPathNodeWrapper
*/
private Stack xPathNodes = new Stack();
/**
* Last {@code XPathNodeWrapper} that was created in the {@link #startElement(javax.xml.namespace.QName)} method (can be an
* element representing primitive value or an attribute so it may not be stored in the {@link #xPathNodes} stack).
*/
private XPathNodeWrapper lastAccessedNode = null;
/**
* Expected type of unmarshalled entity. For {@code MoxyXmlStructure}.
*/
private final Class> expectedType;
/**
* {@code JAXBContext}. For {@code MoxyXmlStructure}.
*/
private final JAXBContext jaxbContext;
/**
* Flag whether the first start element event is being handled by the {@code #startElement} method. If so the method should
* not proceed because the first element is handled in the constructor.
*/
private boolean firstDocumentElement = true;
private final boolean isReader;
public MoxyXmlStructure(final JAXBContext jaxbContext, final Class> expectedType, final boolean isReader) {
super(jaxbContext, expectedType, isReader);
this.jaxbContext = jaxbContext;
this.expectedType = expectedType;
this.isReader = isReader;
}
/**
* Creates a {@code XPathNodeWrapper} for the root element of processing XML. The XML type is derived from
* {@code expectedType} or by using {@code elementName} if the {@code expectedType} is {@link JAXBElement}.
*
* @param elementName name to obtain expected type of the element if the {@code expectedType} of this class is
* {@link JAXBElement}.
*/
private XPathNodeWrapper getRootNodeWrapperForElement(final QName elementName, boolean isRoot) {
if (jaxbContext == null) {
return null;
}
final org.eclipse.persistence.jaxb.JAXBContext moxyJaxbContext = JAXBHelper.getJAXBContext(jaxbContext);
final XMLContext xmlContext = moxyJaxbContext.getXMLContext();
final DatabaseSession session = xmlContext.getSession(0);
Class> expectedType = this.expectedType;
if (!isRoot) {
final HashMap typeToSchemaType = moxyJaxbContext.getTypeToSchemaType();
for (Map.Entry entry : typeToSchemaType.entrySet()) {
if (entry.getValue().getLocalPart().equals(elementName.getLocalPart())) {
expectedType = (Class>) entry.getKey();
break;
}
}
}
if (JAXBElement.class.isAssignableFrom(expectedType)) {
final Map descriptors = session.getDescriptors();
for (Map.Entry descriptor : descriptors.entrySet()) {
final QName defaultRootElementType = ((XMLDescriptor) descriptor.getValue()).getDefaultRootElementType();
// Check local name.
if (defaultRootElementType == null
|| !defaultRootElementType.getLocalPart().contains(elementName.getLocalPart())) {
continue;
}
// Check namespace.
if ((defaultRootElementType.getNamespaceURI() == null && elementName.getNamespaceURI() == null)
|| (defaultRootElementType.getNamespaceURI() != null
&& defaultRootElementType.getNamespaceURI().equals(elementName.getNamespaceURI()))) {
expectedType = descriptor.getKey();
}
}
}
final ClassDescriptor descriptor = session.getDescriptor(expectedType);
if (descriptor != null) {
final TreeObjectBuilder objectBuilder = (TreeObjectBuilder) descriptor.getObjectBuilder();
return new XPathNodeWrapper(objectBuilder.getRootXPathNode(),
null, null, descriptor, new QName(expectedType.getSimpleName()));
}
return null;
}
@Override
public Collection getExpectedElements() {
final List elements = new LinkedList();
final XPathNodeWrapper currentNodeWrapper = getCurrentNodeWrapper();
final Map nonAttributeChildrenMap =
currentNodeWrapper == null ? null : currentNodeWrapper.xPathNode.getNonAttributeChildrenMap();
if (nonAttributeChildrenMap != null) {
for(Map.Entry entry : nonAttributeChildrenMap.entrySet()) {
elements.add(new QName(entry.getKey().getNamespaceURI(), entry.getKey().getLocalName()));
}
}
return elements;
}
/**
* Returns the top {@code XPathNodeWrapper} from the stack.
*
* @return top {@code XPathNodeWrapper} from the stack or {@code null} if the stack is empty.
*/
private XPathNodeWrapper getCurrentNodeWrapper() {
final XPathNodeWrapper nodeWrapper = xPathNodes.isEmpty() ? null : xPathNodes.peek();
if (nodeWrapper != null) {
return nodeWrapper;
} else {
return null;
}
}
@Override
public Collection getExpectedAttributes() {
final List attributes = new LinkedList();
final XPathNodeWrapper currentNodeWrapper = getCurrentNodeWrapper();
final Map attributeChildrenMap =
currentNodeWrapper == null ? null :currentNodeWrapper.xPathNode.getAttributeChildrenMap();
if (attributeChildrenMap != null) {
for(Map.Entry entry : attributeChildrenMap.entrySet()) {
attributes.add(new QName(entry.getKey().getNamespaceURI(), entry.getKey().getLocalName()));
}
}
return attributes;
}
@Override
public void startElement(final QName name) {
if (name == null || firstDocumentElement) {
firstDocumentElement = false;
if (name != null) {
xPathNodes.push(getRootNodeWrapperForElement(name, true));
}
return;
}
XPathNode childNode = null;
XPathNodeWrapper newNodeWrapper = null;
// find our child node
final XPathNodeWrapper currentNodeWrapper = getCurrentNodeWrapper();
final XPathNodeWrapper actualNodeWrapper = currentNodeWrapper.currentType == null ? currentNodeWrapper : currentNodeWrapper.currentType;
final Map nonAttributeChildrenMap =
actualNodeWrapper == null ? null : actualNodeWrapper.xPathNode.getNonAttributeChildrenMap();
if (nonAttributeChildrenMap != null) {
for (Map.Entry child : nonAttributeChildrenMap.entrySet()) {
if (name.getLocalPart().equalsIgnoreCase(child.getKey().getLocalName())) {
childNode = child.getValue();
break;
}
}
if (childNode != null) {
final MappingNodeValue nodeValue = (MappingNodeValue) childNode.getNodeValue();
if (nodeValue != null) {
ClassDescriptor descriptor = nodeValue.getMapping().getReferenceDescriptor();
if (descriptor == null && !isReader) {
descriptor = nodeValue.getMapping().getDescriptor();
}
if (descriptor != null) {
TreeObjectBuilder objectBuilder = (TreeObjectBuilder) descriptor.getObjectBuilder();
final XPathNodeWrapper nodeWrapper = actualNodeWrapper;
newNodeWrapper = new XPathNodeWrapper(
objectBuilder.getRootXPathNode(),
nodeWrapper,
nodeValue, descriptor, name);
xPathNodes.push(newNodeWrapper);
}
}
}
}
lastAccessedNode = newNodeWrapper == null ? new XPathNodeWrapper(name) : newNodeWrapper;
}
@Override
public void endElement(final QName name) {
final XPathNodeWrapper xPathNodeWrapper = getCurrentNodeWrapper();
if (xPathNodeWrapper != null && xPathNodeWrapper.name.equals(name)) {
xPathNodes.pop();
}
lastAccessedNode = getCurrentNodeWrapper();
}
@Override
public Map getExpectedElementsMap() {
return getCurrentNodeWrapper() == null ?
Collections.emptyMap() : getCurrentNodeWrapper().getExpectedElementsMap();
}
@Override
public Map getExpectedAttributesMap() {
return getCurrentNodeWrapper() == null ?
Collections.emptyMap() : getCurrentNodeWrapper().getExpectedAttributesMap();
}
@Override
public Type getEntityType(QName entity, boolean isAttribute) {
return getType(entity, isAttribute, false);
}
@Override
public Type getIndividualType() {
return getContainerType(true);
}
private Type getType(QName entity, boolean isAttribute, boolean isIndividual) {
final XPathNodeWrapper currentNodeWrapper = getCurrentNodeWrapper();
final ClassDescriptor classDescriptor = currentNodeWrapper == null ? null : currentNodeWrapper.getClassDescriptor();
if (classDescriptor != null) {
if (currentNodeWrapper.name.equals(entity)) {
final Type containerType = getContainerType(isIndividual);
return containerType != null ? containerType : classDescriptor.getJavaClass();
} else {
final EntityType entityType = currentNodeWrapper.getEntitiesTypesMap(isAttribute).get(entity.getLocalPart());
return entityType == null ? null : entityType.type;
}
}
return null;
}
private Type getContainerType(final boolean isIndividual) {
final XPathNodeWrapper currentNodeWrapper = getCurrentNodeWrapper();
if (currentNodeWrapper.nodeValue != null && currentNodeWrapper.nodeValue.isContainerValue()) {
final DatabaseMapping mapping = currentNodeWrapper.nodeValue.getMapping();
Converter valueConverter = null;
if (mapping != null) {
if (isIndividual) {
if (mapping instanceof AbstractCompositeDirectCollectionMapping) {
valueConverter = ((AbstractCompositeDirectCollectionMapping) mapping).getValueConverter();
} else if (mapping instanceof XMLCompositeCollectionMapping) {
valueConverter = ((XMLCompositeCollectionMapping) mapping).getConverter();
}
}
if (valueConverter instanceof TypeConversionConverter) {
return ((TypeConversionConverter) valueConverter).getObjectClass();
} else if (mapping.getContainerPolicy() != null) {
return mapping.getContainerPolicy().getContainerClass();
}
}
}
return null;
}
@Override
public boolean isArrayCollection() {
final XPathNodeWrapper currentNodeWrapper = getCurrentNodeWrapper();
if (currentNodeWrapper != null && lastAccessedNode != null
&& lastAccessedNode.name == currentNodeWrapper.name) {
final MappingNodeValue nodeValue = currentNodeWrapper.getNodeValue();
return nodeValue != null && nodeValue.isContainerValue();
} else {
return false;
}
}
@Override
public boolean isSameArrayCollection() {
final int size = xPathNodes.size();
if (size >= 2) {
final XPathNodeWrapper last = xPathNodes.peek();
final XPathNodeWrapper beforeLast = xPathNodes.get(size - 2);
if (last.isInSameArrayAs(beforeLast)) {
return true;
}
}
return false;
}
@Override
public void handleAttribute(final QName attributeName, final String value) {
final String localPart = attributeName.getLocalPart();
if ("@type".equals(localPart) || "type".equals(localPart)) {
getCurrentNodeWrapper().currentType =
getRootNodeWrapperForElement(new QName(value), false);
}
}
@Override
public boolean hasSubElements() {
final Collection expectedElements = getExpectedElements();
return expectedElements != null && !expectedElements.isEmpty();
}
}