| /* |
| * 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: |
| // Matt MacIvor - 2.5.1 - Initial Implementation |
| package org.eclipse.persistence.internal.jaxb.json.schema; |
| |
| import java.lang.reflect.ParameterizedType; |
| import java.lang.reflect.Type; |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import org.eclipse.persistence.descriptors.ClassDescriptor; |
| import org.eclipse.persistence.internal.core.helper.CoreClassConstants; |
| import org.eclipse.persistence.internal.jaxb.json.schema.model.JsonSchema; |
| import org.eclipse.persistence.internal.jaxb.json.schema.model.JsonType; |
| import org.eclipse.persistence.internal.jaxb.json.schema.model.Property; |
| import org.eclipse.persistence.internal.oxm.Constants; |
| import org.eclipse.persistence.internal.oxm.QNameInheritancePolicy; |
| import org.eclipse.persistence.internal.oxm.XMLBinaryDataHelper; |
| import org.eclipse.persistence.internal.oxm.XPathFragment; |
| import org.eclipse.persistence.internal.oxm.mappings.BinaryDataCollectionMapping; |
| import org.eclipse.persistence.internal.oxm.mappings.BinaryDataMapping; |
| import org.eclipse.persistence.internal.oxm.mappings.ChoiceCollectionMapping; |
| import org.eclipse.persistence.internal.oxm.mappings.ChoiceObjectMapping; |
| import org.eclipse.persistence.internal.oxm.mappings.CollectionReferenceMapping; |
| import org.eclipse.persistence.internal.oxm.mappings.CompositeCollectionMapping; |
| import org.eclipse.persistence.internal.oxm.mappings.CompositeObjectMapping; |
| import org.eclipse.persistence.internal.oxm.mappings.DirectCollectionMapping; |
| import org.eclipse.persistence.internal.oxm.mappings.DirectMapping; |
| import org.eclipse.persistence.internal.oxm.mappings.InverseReferenceMapping; |
| import org.eclipse.persistence.internal.oxm.mappings.Mapping; |
| import org.eclipse.persistence.internal.oxm.mappings.ObjectReferenceMapping; |
| import org.eclipse.persistence.internal.oxm.record.namespaces.MapNamespacePrefixMapper; |
| import org.eclipse.persistence.jaxb.JAXBContext; |
| import org.eclipse.persistence.jaxb.JAXBContextProperties; |
| import org.eclipse.persistence.jaxb.JAXBEnumTypeConverter; |
| import org.eclipse.persistence.jaxb.MarshallerProperties; |
| import org.eclipse.persistence.mappings.DatabaseMapping; |
| import org.eclipse.persistence.oxm.NamespacePrefixMapper; |
| import org.eclipse.persistence.oxm.NamespaceResolver; |
| import org.eclipse.persistence.oxm.XMLContext; |
| import org.eclipse.persistence.oxm.XMLDescriptor; |
| import org.eclipse.persistence.oxm.XMLField; |
| import org.eclipse.persistence.oxm.mappings.XMLAnyAttributeMapping; |
| import org.eclipse.persistence.oxm.mappings.XMLAnyCollectionMapping; |
| import org.eclipse.persistence.oxm.mappings.XMLAnyObjectMapping; |
| import org.eclipse.persistence.oxm.mappings.XMLFragmentCollectionMapping; |
| import org.eclipse.persistence.oxm.mappings.XMLFragmentMapping; |
| import org.eclipse.persistence.sessions.Project; |
| |
| /** |
| * INTERNAL: |
| * <p><b>Purpose:</b> This class generates an instance of JsonSchema based on an EclipseLink |
| * project and given a root class. The descriptor for the root class' mappings are traversed and |
| * the associated schema artifacts are created. |
| * |
| */ |
| public class JsonSchemaGenerator { |
| Project project; |
| JsonSchema schema; |
| Map contextProperties; |
| String attributePrefix; |
| Class<?> rootClass; |
| boolean namespaceAware; |
| NamespaceResolver resolver; |
| String NAMESPACE_SEPARATOR = "."; |
| NamespacePrefixMapper prefixMapper = null; |
| Property[] xopIncludeProp = null; |
| XMLContext xmlContext; |
| Property rootProperty = null; |
| private JAXBContext jaxbContext; |
| |
| private static String DEFINITION_PATH="#/definitions"; |
| |
| private static HashMap<Class, JsonType> javaTypeToJsonType; |
| |
| |
| public JsonSchemaGenerator(JAXBContext jaxbContext, Map properties) { |
| //this.project = project; |
| this.xmlContext = jaxbContext.getXMLContext(); |
| this.jaxbContext = jaxbContext; |
| this.contextProperties = properties; |
| if(properties != null) { |
| attributePrefix = (String)properties.get(JAXBContextProperties.JSON_ATTRIBUTE_PREFIX); |
| Object prefixMapperValue = properties.get(JAXBContextProperties.NAMESPACE_PREFIX_MAPPER); |
| if(prefixMapperValue != null) { |
| if(prefixMapperValue instanceof Map){ |
| prefixMapper = new MapNamespacePrefixMapper((Map)prefixMapperValue); |
| }else{ |
| prefixMapper = (NamespacePrefixMapper)prefixMapperValue; |
| } |
| } |
| if(prefixMapper != null) { |
| //check for customer separator |
| String namespaceSeparator = (String) properties.get(JAXBContextProperties.JSON_NAMESPACE_SEPARATOR); |
| if(namespaceSeparator != null) { |
| this.NAMESPACE_SEPARATOR = namespaceSeparator; |
| } |
| } |
| } |
| } |
| |
| public JsonSchema generateSchema(Class<?> rootClass) { |
| this.rootClass = rootClass; |
| schema = new JsonSchema(); |
| schema.setTitle(rootClass.getName()); |
| |
| if(rootClass.isEnum()) { |
| Class<?> generatedWrapper = jaxbContext.getClassToGeneratedClasses().get(rootClass.getName()); |
| if(generatedWrapper != null) { |
| rootClass = generatedWrapper; |
| } else { |
| schema.setType(JsonType.STRING); |
| return schema; |
| } |
| } |
| //Check for simple type |
| JsonType rootType = getJsonTypeForJavaType(rootClass); |
| if(rootType != JsonType.OBJECT) { |
| if(rootType == JsonType.BINARYTYPE) { |
| schema.setAnyOf(getXopIncludeProperties()); |
| return schema; |
| } else { |
| schema.setType(rootType); |
| return schema; |
| } |
| } |
| |
| Map<String, Property> properties = null; |
| //Check for root level list or array |
| if(rootClass.isArray() || isCollection(rootClass)) { |
| schema.setType(JsonType.ARRAY); |
| schema.setItems(new Property()); |
| Class<?> itemType = Object.class; |
| |
| if(rootClass.isArray()) { |
| itemType = rootClass.getComponentType(); |
| } else { |
| Type pType = rootClass.getGenericSuperclass(); |
| if(pType instanceof ParameterizedType) { |
| itemType = (Class<?>)((ParameterizedType)pType).getActualTypeArguments()[0]; |
| } |
| } |
| rootType = getJsonTypeForJavaType(itemType); |
| schema.getItems().setType(rootType); |
| if(rootType != JsonType.OBJECT) { |
| return schema; |
| } |
| rootClass = itemType; |
| properties = schema.getItems().getProperties(); |
| |
| } else { |
| schema.setType(JsonType.OBJECT); |
| properties = schema.getProperties(); |
| } |
| this.project = this.xmlContext.getSession(rootClass).getProject(); |
| |
| XMLDescriptor descriptor = (XMLDescriptor)project.getDescriptor(rootClass); |
| |
| Boolean includeRoot = Boolean.TRUE; |
| if(contextProperties != null) { |
| includeRoot = (Boolean) this.contextProperties.get(JAXBContextProperties.JSON_INCLUDE_ROOT); |
| if(includeRoot == null) { |
| includeRoot = Boolean.TRUE; |
| } |
| } |
| if(Boolean.TRUE.equals(includeRoot)) { |
| XMLField field = descriptor.getDefaultRootElementField(); |
| if(field != null) { |
| rootProperty = new Property(); |
| rootProperty.setType(JsonType.OBJECT); |
| rootProperty.setName(getNameForFragment(field.getXPathFragment())); |
| properties.put(rootProperty.getName(), rootProperty); |
| properties = rootProperty.getProperties(); |
| } |
| } |
| |
| boolean allowsAdditionalProperties = hasAnyMappings(descriptor); |
| if(descriptor.hasInheritance()) { |
| //handle inheritence |
| //schema.setAnyOf(new Property[descriptor.getInheritancePolicy().getAllChildDescriptors().size()]); |
| List<ClassDescriptor> descriptors = this.getAllDescriptorsForInheritance(descriptor); |
| Property[] props = new Property[descriptors.size()]; |
| for(int i = 0; i < props.length; i++) { |
| XMLDescriptor nextDescriptor = (XMLDescriptor)descriptors.get(i); |
| |
| Property ref = new Property(); |
| ref.setRef(getReferenceForDescriptor(nextDescriptor, true)); |
| props[i] = ref; |
| } |
| if(rootProperty != null) { |
| rootProperty.setAnyOf(props); |
| rootProperty.setProperties(null); |
| rootProperty.setType(null); |
| rootProperty.setAdditionalProperties(null); |
| rootProperty.setAdditionalProperties(null); |
| } else { |
| this.schema.setAnyOf(props); |
| this.schema.setProperties(null); |
| this.schema.setType(null); |
| this.schema.setAdditionalProperties(null); |
| } |
| } else { |
| JsonType type = populateProperties(properties, descriptor); |
| if(type != null) { |
| if(type == JsonType.BINARYTYPE) { |
| if(rootProperty != null) { |
| rootProperty.setAnyOf(getXopIncludeProperties()); |
| rootProperty.setProperties(null); |
| rootProperty.setAdditionalProperties(null); |
| rootProperty.setType(null); |
| } else { |
| this.schema.setAnyOf(getXopIncludeProperties()); |
| this.schema.setProperties(null); |
| this.schema.setType(null); |
| this.schema.setAdditionalProperties(null); |
| } |
| } else if(type == JsonType.ENUMTYPE) { |
| if(rootProperty != null) { |
| rootProperty.setType(JsonType.STRING); |
| rootProperty.setProperties(null); |
| rootProperty.setEnumeration(getEnumeration(descriptor)); |
| } else { |
| this.schema.setType(JsonType.STRING); |
| this.schema.setProperties(null); |
| this.schema.setEnumeration(getEnumeration(descriptor)); |
| } |
| } else { |
| if(rootProperty != null) { |
| rootProperty.setType(type); |
| } else { |
| schema.setType(type); |
| } |
| } |
| } else { |
| if(rootProperty != null) { |
| rootProperty.setAdditionalProperties(allowsAdditionalProperties); |
| } else { |
| this.schema.setAdditionalProperties(allowsAdditionalProperties); |
| } |
| } |
| } |
| return schema; |
| } |
| |
| private List<String> getEnumeration(XMLDescriptor desc) { |
| return getEnumeration(getTextMapping(desc)); |
| } |
| |
| private List<String> getEnumeration(DatabaseMapping textMapping) { |
| JAXBEnumTypeConverter converter = null; |
| if(textMapping.isAbstractDirectMapping()) { |
| converter = (JAXBEnumTypeConverter) ((DirectMapping)textMapping).getConverter(); |
| } else if(textMapping.isAbstractCompositeDirectCollectionMapping()) { |
| converter = (JAXBEnumTypeConverter) ((DirectCollectionMapping)textMapping).getValueConverter(); |
| } |
| if(converter == null) { |
| return null; |
| } |
| List<String> enumeration = new ArrayList<String>(); |
| for(Object nextValue: converter.getAttributeToFieldValues().values()) { |
| enumeration.add(nextValue.toString()); |
| } |
| return enumeration; |
| } |
| |
| private boolean hasAnyMappings(XMLDescriptor descriptor) { |
| for(DatabaseMapping next:descriptor.getMappings()) { |
| if(next instanceof XMLAnyAttributeMapping || |
| next instanceof XMLAnyObjectMapping || |
| next instanceof XMLAnyCollectionMapping || |
| next instanceof XMLFragmentCollectionMapping || |
| next instanceof XMLFragmentMapping |
| |
| ) { |
| return true; |
| } else if(next instanceof CompositeCollectionMapping) { |
| CompositeCollectionMapping ccm = (CompositeCollectionMapping)next; |
| if(ccm.getReferenceDescriptor() == null && ((XMLField)ccm.getField()).isSelfField()) { |
| return true; |
| } |
| } else if(next instanceof CompositeObjectMapping) { |
| CompositeObjectMapping ccm = (CompositeObjectMapping)next; |
| if(ccm.getReferenceDescriptor() == null && ((XMLField)ccm.getField()).isSelfField()) { |
| return true; |
| } |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * Populates the map of properties based on the mappings in this descriptor. If the descriptor represents a |
| * simple type (a single direct mapping with xpath of text) then the name of the simple type is returned. |
| * Otherwise null is returned. |
| * |
| * @return null for a complex type, the simple type name for a simple type. |
| */ |
| private JsonType populateProperties(Map<String, Property> properties, XMLDescriptor descriptor) { |
| |
| List<DatabaseMapping> mappings = descriptor.getMappings(); |
| if(mappings == null || mappings.isEmpty()) { |
| return null; |
| } |
| |
| if(isSimpleType(descriptor)) { |
| //check for simple type |
| DatabaseMapping mapping = getTextMapping(descriptor); |
| if(mapping.isAbstractDirectMapping()) { |
| DirectMapping directMapping = (DirectMapping)mapping; |
| if(directMapping.getConverter() instanceof JAXBEnumTypeConverter) { |
| return JsonType.ENUMTYPE; |
| } |
| return getJsonTypeForJavaType(directMapping.getAttributeClassification()); |
| } else if(mapping.isAbstractCompositeDirectCollectionMapping()) { |
| DirectCollectionMapping directMapping = (DirectCollectionMapping)mapping; |
| if(directMapping.getValueConverter() instanceof JAXBEnumTypeConverter) { |
| return JsonType.ENUMTYPE; |
| } |
| Class<?> type = directMapping.getAttributeElementClass(); |
| if(type == null) { |
| type = CoreClassConstants.STRING; |
| } |
| return getJsonTypeForJavaType(type); |
| } else { |
| //only other option is binary |
| return JsonType.BINARYTYPE; |
| } |
| } |
| for(DatabaseMapping next:mappings) { |
| if(next instanceof ChoiceObjectMapping) { |
| ChoiceObjectMapping coMapping = (ChoiceObjectMapping)next; |
| for(Object nestedMapping:coMapping.getChoiceElementMappingsByClass().values()) { |
| Property prop = generateProperty((Mapping)nestedMapping, descriptor, properties); |
| if(prop != null && !(properties.containsKey(prop.getName()))) { |
| properties.put(prop.getName(), prop); |
| } |
| } |
| } else if(next instanceof ChoiceCollectionMapping) { |
| ChoiceCollectionMapping coMapping = (ChoiceCollectionMapping)next; |
| for(Object nestedMapping:coMapping.getChoiceElementMappingsByClass().values()) { |
| Property prop = generateProperty((Mapping)nestedMapping, descriptor, properties); |
| if(prop != null && !(properties.containsKey(prop.getName()))) { |
| properties.put(prop.getName(), prop); |
| } |
| } |
| } else { |
| Property prop = generateProperty((Mapping)next, descriptor, properties); |
| if(prop != null && !(properties.containsKey(prop.getName()))) { |
| properties.put(prop.getName(), prop); |
| } |
| } |
| } |
| return null; |
| } |
| |
| private DatabaseMapping getTextMapping(XMLDescriptor descriptor) { |
| for(DatabaseMapping next:descriptor.getMappings()) { |
| if(next.isAbstractDirectMapping()) { |
| DirectMapping mapping = (DirectMapping)next; |
| if(((XMLField)mapping.getField()).getXPathFragment().nameIsText()) { |
| return next; |
| } |
| } |
| if(next.isAbstractCompositeDirectCollectionMapping()) { |
| DirectCollectionMapping mapping = (DirectCollectionMapping)next; |
| if(((XMLField)mapping.getField()).getXPathFragment().nameIsText()) { |
| return next; |
| } |
| } |
| if(next instanceof BinaryDataMapping) { |
| BinaryDataMapping mapping = (BinaryDataMapping)next; |
| if(((XMLField)mapping.getField()).isSelfField()) { |
| return next; |
| } |
| } |
| if(next instanceof BinaryDataCollectionMapping) { |
| BinaryDataCollectionMapping mapping = (BinaryDataCollectionMapping)next; |
| if(((XMLField)mapping.getField()).isSelfField()) { |
| return next; |
| } |
| } |
| } |
| return null; |
| } |
| |
| private boolean isSimpleType(XMLDescriptor descriptor) { |
| DatabaseMapping mapping = null; |
| if(descriptor.getMappings().size() == 1) { |
| mapping = descriptor.getMappings().get(0); |
| } else if(descriptor.getMappings().size() == 2) { |
| boolean hasInverseRef = false; |
| for(DatabaseMapping next:descriptor.getMappings()) { |
| if(next instanceof InverseReferenceMapping) { |
| hasInverseRef = true; |
| } else { |
| mapping = next; |
| } |
| } |
| if(!hasInverseRef) { |
| return false; |
| } |
| } else { |
| return false; |
| } |
| |
| if(mapping.isAbstractDirectMapping()) { |
| if(((XMLField)mapping.getField()).getXPathFragment().nameIsText()) { |
| return true; |
| } |
| } |
| if(mapping.isAbstractCompositeDirectCollectionMapping()) { |
| if(((XMLField)mapping.getField()).getXPathFragment().nameIsText()) { |
| return true; |
| } |
| } |
| if(mapping instanceof BinaryDataMapping) { |
| if(((XMLField)mapping.getField()).isSelfField()) { |
| return true; |
| } |
| } |
| if(mapping instanceof BinaryDataCollectionMapping) { |
| if(((XMLField)mapping.getField()).isSelfField()) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| private Property generateProperty(Mapping next, XMLDescriptor descriptor, Map<String, Property> properties) { |
| Property prop = null; |
| if(next.isCollectionMapping()) { |
| if(next instanceof CollectionReferenceMapping) { |
| CollectionReferenceMapping mapping = (CollectionReferenceMapping)next; |
| Set<XMLField> sourceFields = mapping.getSourceToTargetKeyFieldAssociations().keySet(); |
| XMLDescriptor reference = (XMLDescriptor) mapping.getReferenceDescriptor(); |
| for(XMLField nextField: sourceFields) { |
| XPathFragment frag = nextField.getXPathFragment(); |
| String propertyName = getNameForFragment(frag); |
| |
| XMLField targetField = (XMLField) mapping.getSourceToTargetKeyFieldAssociations().get(nextField); |
| Class<?> type = CoreClassConstants.STRING; |
| if(reference != null) { |
| type = getTypeForTargetField(targetField, reference); |
| } |
| |
| prop = properties.get(propertyName); |
| if(prop == null) { |
| prop = new Property(); |
| prop.setName(propertyName); |
| } |
| Property nestedProperty = getNestedPropertyForFragment(frag, prop); |
| nestedProperty.setType(JsonType.ARRAY); |
| nestedProperty.setItem(new Property()); |
| nestedProperty.getItem().setType(getJsonTypeForJavaType(type)); |
| if(!properties.containsKey(prop.getName())) { |
| properties.put(prop.getName(), prop); |
| } |
| } |
| return prop; |
| } else if(next.isAbstractCompositeCollectionMapping()) { |
| CompositeCollectionMapping mapping = (CompositeCollectionMapping)next; |
| XMLField field = (XMLField)mapping.getField(); |
| XPathFragment frag = field.getXPathFragment(); |
| String propName = getNameForFragment(frag); |
| //for paths, there may already be an existing property |
| prop = properties.get(propName); |
| if(prop == null) { |
| prop = new Property(); |
| prop.setName(propName); |
| } |
| Property nestedProperty = getNestedPropertyForFragment(frag, prop); |
| nestedProperty.setType(JsonType.ARRAY); |
| nestedProperty.setItem(new Property()); |
| XMLDescriptor referenceDescriptor = (XMLDescriptor)mapping.getReferenceDescriptor(); |
| if(referenceDescriptor != null && referenceDescriptor.hasInheritance()) { |
| //handle inheritence |
| //schema.setAnyOf(new Property[descriptor.getInheritancePolicy().getAllChildDescriptors().size()]); |
| List<ClassDescriptor> descriptors = getAllDescriptorsForInheritance(referenceDescriptor); |
| Property[] props = new Property[descriptors.size()]; |
| for(int i = 0; i < props.length; i++) { |
| XMLDescriptor nextDescriptor = null; |
| nextDescriptor = (XMLDescriptor)descriptors.get(i); |
| Property ref = new Property(); |
| ref.setRef(getReferenceForDescriptor(nextDescriptor, true)); |
| props[i] = ref; |
| } |
| nestedProperty.getItem().setAnyOf(props); |
| } else { |
| nestedProperty.getItem().setRef(getReferenceForDescriptor(referenceDescriptor, false)); |
| //populateProperties(nestedProperty.getItem().getProperties(), (XMLDescriptor)mapping.getReferenceDescriptor()); |
| } |
| } else if(next.isAbstractCompositeDirectCollectionMapping()) { |
| DirectCollectionMapping mapping = (DirectCollectionMapping)next; |
| XMLField field = (XMLField)mapping.getField(); |
| XPathFragment frag = field.getXPathFragment(); |
| List<String> enumeration = null; |
| if(mapping.getValueConverter() instanceof JAXBEnumTypeConverter) { |
| enumeration = getEnumeration((DatabaseMapping)next); |
| } |
| String propertyName = getNameForFragment(frag); |
| if(frag.nameIsText()) { |
| propertyName = (String)this.contextProperties.get(MarshallerProperties.JSON_VALUE_WRAPPER); |
| } |
| |
| if(frag.isAttribute() && this.attributePrefix != null) { |
| propertyName = attributePrefix + propertyName; |
| } |
| prop = properties.get(propertyName); |
| if(prop == null) { |
| prop = new Property(); |
| prop.setName(propertyName); |
| } |
| Property nestedProperty = getNestedPropertyForFragment(frag, prop); |
| nestedProperty.setType(JsonType.ARRAY); |
| nestedProperty.setItem(new Property()); |
| if(enumeration != null) { |
| nestedProperty.getItem().setEnumeration(enumeration); |
| } |
| Class<?> type = mapping.getAttributeElementClass(); |
| if(type == null) { |
| type = CoreClassConstants.STRING; |
| } |
| nestedProperty.getItem().setType(getJsonTypeForJavaType(type)); |
| return prop; |
| } else if(next instanceof BinaryDataCollectionMapping) { |
| BinaryDataCollectionMapping mapping = (BinaryDataCollectionMapping)next; |
| XMLField field = (XMLField)mapping.getField(); |
| XPathFragment frag = field.getXPathFragment(); |
| |
| String propertyName = getNameForFragment(frag); |
| if(frag.isSelfFragment()) { |
| propertyName = Constants.VALUE_WRAPPER; |
| if(this.contextProperties != null) { |
| String valueWrapper = (String) this.contextProperties.get(JAXBContextProperties.JSON_VALUE_WRAPPER); |
| if(valueWrapper != null) { |
| propertyName = valueWrapper; |
| } |
| } |
| } |
| |
| if(frag.isAttribute() && this.attributePrefix != null) { |
| propertyName = attributePrefix + propertyName; |
| } |
| prop = properties.get(propertyName); |
| if(prop == null) { |
| prop = new Property(); |
| prop.setName(propertyName); |
| } |
| Property nestedProperty = getNestedPropertyForFragment(frag, prop); |
| nestedProperty.setType(JsonType.ARRAY); |
| nestedProperty.setItem(new Property()); |
| |
| if(mapping.shouldInlineBinaryData()) { |
| nestedProperty.getItem().setType(JsonType.STRING); |
| } else { |
| nestedProperty.getItem().setAnyOf(getXopIncludeProperties()); |
| } |
| return prop; |
| } |
| } else { |
| if(next.isAbstractDirectMapping()) { |
| //handle direct mapping |
| DirectMapping directMapping = (DirectMapping)next; |
| XMLField field = (XMLField)directMapping.getField(); |
| XPathFragment frag = field.getXPathFragment(); |
| List<String> enumeration = null; |
| if(directMapping.getConverter() instanceof JAXBEnumTypeConverter) { |
| enumeration = getEnumeration((DatabaseMapping)directMapping); |
| } |
| String propertyName = getNameForFragment(frag); |
| if(frag.nameIsText()) { |
| propertyName = Constants.VALUE_WRAPPER; |
| if(this.contextProperties != null) { |
| String valueWrapper = (String) this.contextProperties.get(JAXBContextProperties.JSON_VALUE_WRAPPER); |
| if(valueWrapper != null) { |
| propertyName = valueWrapper; |
| } |
| } |
| } |
| if(frag.isAttribute() && this.attributePrefix != null) { |
| propertyName = attributePrefix + propertyName; |
| } |
| prop = properties.get(propertyName); |
| if(prop == null) { |
| prop = new Property(); |
| prop.setName(propertyName); |
| } |
| Property nestedProperty = getNestedPropertyForFragment(frag, prop); |
| if(enumeration != null) { |
| nestedProperty.setEnumeration(enumeration); |
| } |
| if(directMapping instanceof BinaryDataMapping) { |
| BinaryDataMapping binaryMapping = (BinaryDataMapping)directMapping; |
| if(binaryMapping.shouldInlineBinaryData() || binaryMapping.isSwaRef()) { |
| nestedProperty.setType(JsonType.STRING); |
| } else { |
| if(this.xopIncludeProp == null) { |
| initXopIncludeProp(); |
| } |
| nestedProperty.setAnyOf(this.xopIncludeProp); |
| } |
| } else { |
| nestedProperty.setType(getJsonTypeForJavaType(directMapping.getAttributeClassification())); |
| } |
| return prop; |
| } else if(next instanceof ObjectReferenceMapping) { |
| ObjectReferenceMapping mapping = (ObjectReferenceMapping)next; |
| Set<XMLField> sourceFields = mapping.getSourceToTargetKeyFieldAssociations().keySet(); |
| XMLDescriptor reference = (XMLDescriptor) mapping.getReferenceDescriptor(); |
| for(XMLField nextField: sourceFields) { |
| XPathFragment frag = nextField.getXPathFragment(); |
| String propName = getNameForFragment(frag); |
| XMLField targetField = (XMLField) mapping.getSourceToTargetKeyFieldAssociations().get(nextField); |
| Class<?> type = CoreClassConstants.STRING; |
| if(reference != null) { |
| type = getTypeForTargetField(targetField, reference); |
| } |
| |
| prop = properties.get(propName); |
| if(prop == null) { |
| prop = new Property(); |
| prop.setName(propName); |
| } |
| Property nestedProperty = getNestedPropertyForFragment(frag, prop); |
| //nestedProperty.setType(JsonType.ARRAY); |
| //nestedProperty.setItem(new Property()); |
| nestedProperty.setType(getJsonTypeForJavaType(type)); |
| if(!properties.containsKey(prop.getName())) { |
| properties.put(prop.getName(), prop); |
| } |
| } |
| return prop; |
| } else if(next.isAbstractCompositeObjectMapping()) { |
| CompositeObjectMapping mapping = (CompositeObjectMapping)next; |
| XMLDescriptor nextDescriptor = (XMLDescriptor)mapping.getReferenceDescriptor(); |
| XMLField field = (XMLField)mapping.getField(); |
| XPathFragment firstFragment = field.getXPathFragment(); |
| if(firstFragment.isSelfFragment() || firstFragment.nameIsText()) { |
| if(nextDescriptor != null) { |
| populateProperties(properties, nextDescriptor); |
| } |
| } else { |
| String propName = getNameForFragment(firstFragment); |
| prop = properties.get(propName); |
| if(prop == null) { |
| prop = new Property(); |
| prop.setName(propName); |
| } |
| //prop.setType(JsonType.OBJECT); |
| prop.setName(propName); |
| Property nestedProperty = getNestedPropertyForFragment(firstFragment, prop); |
| XMLDescriptor referenceDescriptor = (XMLDescriptor)mapping.getReferenceDescriptor(); |
| if(referenceDescriptor != null && referenceDescriptor.hasInheritance()) { |
| //handle inheritence |
| //schema.setAnyOf(new Property[descriptor.getInheritancePolicy().getAllChildDescriptors().size()]); |
| List<ClassDescriptor> descriptors = getAllDescriptorsForInheritance(referenceDescriptor); |
| Property[] props = new Property[descriptors.size()]; |
| for(int i = 0; i < props.length; i++) { |
| XMLDescriptor nextDesc = (XMLDescriptor)descriptors.get(i); |
| Property ref = new Property(); |
| ref.setRef(getReferenceForDescriptor(nextDesc, true)); |
| props[i] = ref; |
| } |
| nestedProperty.setAnyOf(props); |
| } else { |
| nestedProperty.setRef(getReferenceForDescriptor(referenceDescriptor, false)); |
| //populateProperties(nestedProperty.getItem().getProperties(), (XMLDescriptor)mapping.getReferenceDescriptor()); |
| } //populateProperties(nestedProperty.getProperties(), nextDescriptor); |
| } |
| } else if(next instanceof BinaryDataMapping) { |
| BinaryDataMapping binaryMapping = (BinaryDataMapping)next; |
| XMLField field = (XMLField)binaryMapping.getField(); |
| XPathFragment frag = field.getXPathFragment(); |
| String propertyName = getNameForFragment(frag); |
| if(frag.isSelfFragment()) { |
| propertyName = Constants.VALUE_WRAPPER; |
| if(this.contextProperties != null) { |
| String valueWrapper = (String) this.contextProperties.get(MarshallerProperties.JSON_VALUE_WRAPPER); |
| if(valueWrapper != null) { |
| propertyName = valueWrapper; |
| } |
| } |
| } |
| if(frag.isAttribute() && this.attributePrefix != null) { |
| propertyName = attributePrefix + propertyName; |
| } |
| prop = properties.get(propertyName); |
| if(prop == null) { |
| prop = new Property(); |
| prop.setName(propertyName); |
| } |
| Property nestedProperty = getNestedPropertyForFragment(frag, prop); |
| if(binaryMapping.shouldInlineBinaryData() || binaryMapping.isSwaRef()) { |
| nestedProperty.setType(JsonType.STRING); |
| } else { |
| if(this.xopIncludeProp == null) { |
| initXopIncludeProp(); |
| } |
| nestedProperty.setAnyOf(this.xopIncludeProp); |
| } |
| return prop; |
| } |
| } |
| |
| return prop; |
| } |
| |
| private String getNameForFragment(XPathFragment frag) { |
| String name = frag.getLocalName(); |
| |
| if(this.prefixMapper != null) { |
| String namespaceUri = frag.getNamespaceURI(); |
| if(namespaceUri != null && namespaceUri.length() != 0) { |
| String prefix = prefixMapper.getPreferredPrefix(namespaceUri, null, true); |
| if(prefix != null) { |
| name = prefix + NAMESPACE_SEPARATOR + name; |
| } |
| } |
| } |
| return name; |
| } |
| |
| private void initXopIncludeProp() { |
| this.xopIncludeProp = new Property[2]; |
| Property p = new Property(); |
| p.setType(JsonType.STRING); |
| this.xopIncludeProp[0] = p; |
| |
| p = new Property(); |
| this.xopIncludeProp[1] = p; |
| p.setType(JsonType.OBJECT); |
| Property includeProperty = new Property(); |
| includeProperty.setName("Include"); |
| includeProperty.setType(JsonType.OBJECT); |
| p.getProperties().put(includeProperty.getName(), includeProperty); |
| |
| Property hrefProp = new Property(); |
| String propName = "href"; |
| if(this.attributePrefix != null) { |
| propName = this.attributePrefix + propName; |
| } |
| hrefProp.setName(propName); |
| hrefProp.setType(JsonType.STRING); |
| includeProperty.getProperties().put(propName, hrefProp); |
| |
| |
| } |
| |
| |
| |
| private String getReferenceForDescriptor(XMLDescriptor referenceDescriptor, boolean generateRoot) { |
| if(referenceDescriptor == null) { |
| return null; |
| } |
| String className = referenceDescriptor.getJavaClass().getSimpleName(); |
| String referenceName = DEFINITION_PATH + "/" + className; |
| |
| if(referenceDescriptor.getJavaClass() == this.rootClass && !generateRoot) { |
| String ref = "#"; |
| if(this.rootProperty != null) { |
| ref += "/properties/" + rootProperty.getName(); |
| } |
| return ref; |
| } |
| if(!this.schema.getDefinitions().containsKey(className)) { |
| Property definition = new Property(); |
| definition.setName(className); |
| this.schema.getDefinitions().put(definition.getName(), definition); |
| definition.setType(JsonType.OBJECT); |
| if(referenceDescriptor.hasInheritance() && referenceDescriptor.getInheritancePolicy().hasClassIndicator()) { |
| XMLField f = (XMLField)referenceDescriptor.getInheritancePolicy().getClassIndicatorField(); |
| Property indicatorProp = new Property(); |
| indicatorProp.setName(getNameForFragment(f.getXPathFragment())); |
| indicatorProp.setType(JsonType.STRING); |
| definition.getProperties().put(indicatorProp.getName(), indicatorProp); |
| } |
| JsonType jType = populateProperties(definition.getProperties(), referenceDescriptor); |
| if(jType != null) { |
| if(jType == JsonType.BINARYTYPE) { |
| definition.setAnyOf(getXopIncludeProperties()); |
| definition.setProperties(null); |
| definition.setAdditionalProperties(null); |
| definition.setType(null); |
| } else { |
| //this represents a simple type |
| definition.setType(jType); |
| definition.setProperties(null); |
| } |
| } |
| definition.setAdditionalProperties(hasAnyMappings(referenceDescriptor)); |
| } |
| // TODO Auto-generated method stub |
| return referenceName; |
| } |
| |
| private Class<?> getTypeForTargetField(XMLField targetField, XMLDescriptor reference) { |
| for(DatabaseMapping next: reference.getMappings()) { |
| if(next.isDirectToFieldMapping()) { |
| DirectMapping directMapping = (DirectMapping)next; |
| if(directMapping.getField().equals(targetField)) { |
| return directMapping.getAttributeClassification(); |
| } |
| } |
| } |
| return null; |
| } |
| |
| private JsonType getJsonTypeForJavaType(Class<?> attributeClassification) { |
| if(attributeClassification.isEnum()) { |
| return JsonType.ENUMTYPE; |
| } |
| HashMap<Class, JsonType> types = getJavaTypeToJsonType(); |
| JsonType jsonType = types.get(attributeClassification); |
| if(jsonType == null) { |
| return JsonType.OBJECT; |
| } |
| return jsonType; |
| } |
| |
| private static HashMap<Class, JsonType> getJavaTypeToJsonType() { |
| if(javaTypeToJsonType == null) { |
| initJavaTypeToJsonType(); |
| } |
| return javaTypeToJsonType; |
| } |
| |
| private static void initJavaTypeToJsonType() { |
| javaTypeToJsonType = new HashMap<Class, JsonType>(); |
| javaTypeToJsonType.put(CoreClassConstants.APBYTE, JsonType.ARRAY); |
| javaTypeToJsonType.put(CoreClassConstants.BIGDECIMAL, JsonType.NUMBER); |
| javaTypeToJsonType.put(CoreClassConstants.BIGINTEGER, JsonType.INTEGER); |
| javaTypeToJsonType.put(CoreClassConstants.PBOOLEAN, JsonType.BOOLEAN); |
| javaTypeToJsonType.put(CoreClassConstants.PBYTE, JsonType.NUMBER); |
| javaTypeToJsonType.put(CoreClassConstants.CALENDAR, JsonType.STRING); |
| javaTypeToJsonType.put(CoreClassConstants.PDOUBLE, JsonType.NUMBER); |
| javaTypeToJsonType.put(CoreClassConstants.PFLOAT, JsonType.NUMBER); |
| javaTypeToJsonType.put(CoreClassConstants.PINT, JsonType.INTEGER); |
| javaTypeToJsonType.put(CoreClassConstants.PLONG, JsonType.NUMBER); |
| javaTypeToJsonType.put(CoreClassConstants.PSHORT, JsonType.NUMBER); |
| javaTypeToJsonType.put(CoreClassConstants.STRING, JsonType.STRING); |
| javaTypeToJsonType.put(CoreClassConstants.CHAR, JsonType.STRING); |
| // other pairs |
| javaTypeToJsonType.put(CoreClassConstants.ABYTE, JsonType.ARRAY); |
| javaTypeToJsonType.put(CoreClassConstants.BOOLEAN, JsonType.BOOLEAN); |
| javaTypeToJsonType.put(CoreClassConstants.BYTE, JsonType.NUMBER); |
| javaTypeToJsonType.put(CoreClassConstants.CLASS, JsonType.STRING); |
| javaTypeToJsonType.put(CoreClassConstants.GREGORIAN_CALENDAR, JsonType.STRING); |
| javaTypeToJsonType.put(CoreClassConstants.DOUBLE, JsonType.NUMBER); |
| javaTypeToJsonType.put(CoreClassConstants.FLOAT, JsonType.NUMBER); |
| javaTypeToJsonType.put(CoreClassConstants.INTEGER, JsonType.INTEGER); |
| javaTypeToJsonType.put(CoreClassConstants.LONG, JsonType.NUMBER); |
| javaTypeToJsonType.put(CoreClassConstants.OBJECT, JsonType.OBJECT); |
| javaTypeToJsonType.put(CoreClassConstants.SHORT, JsonType.NUMBER); |
| javaTypeToJsonType.put(CoreClassConstants.UTILDATE, JsonType.STRING); |
| javaTypeToJsonType.put(CoreClassConstants.SQLDATE, JsonType.STRING); |
| javaTypeToJsonType.put(CoreClassConstants.TIME, JsonType.STRING); |
| javaTypeToJsonType.put(CoreClassConstants.TIMESTAMP, JsonType.STRING); |
| javaTypeToJsonType.put(CoreClassConstants.DURATION, JsonType.STRING); |
| javaTypeToJsonType.put(Constants.QNAME_CLASS, JsonType.STRING); |
| javaTypeToJsonType.put(Constants.URI, JsonType.STRING); |
| javaTypeToJsonType.put(Constants.UUID, JsonType.STRING); |
| |
| javaTypeToJsonType.put(XMLBinaryDataHelper.getXMLBinaryDataHelper().DATA_HANDLER, JsonType.BINARYTYPE); |
| javaTypeToJsonType.put(XMLBinaryDataHelper.getXMLBinaryDataHelper().IMAGE, JsonType.BINARYTYPE); |
| javaTypeToJsonType.put(XMLBinaryDataHelper.getXMLBinaryDataHelper().SOURCE, JsonType.BINARYTYPE); |
| javaTypeToJsonType.put(XMLBinaryDataHelper.getXMLBinaryDataHelper().MULTIPART, JsonType.BINARYTYPE); |
| |
| } |
| |
| private Property getNestedPropertyForFragment(XPathFragment frag, Property prop) { |
| if(frag.getNextFragment() == null || frag.getNextFragment().nameIsText() ) { |
| return prop; |
| } |
| Map<String, Property> currentProperties = prop.getProperties(); |
| prop.setProperties(currentProperties); |
| prop.setType(JsonType.OBJECT); |
| frag = frag.getNextFragment(); |
| String propertyName = getNameForFragment(frag); |
| if(frag.isAttribute() && this.attributePrefix != null) { |
| propertyName = this.attributePrefix + "propertyName"; |
| } |
| while(frag != null && !frag.nameIsText()) { |
| Property nestedProperty = prop.getProperty(propertyName); |
| if(nestedProperty == null) { |
| nestedProperty = new Property(); |
| nestedProperty.setName(propertyName); |
| } |
| currentProperties.put(nestedProperty.getName(), nestedProperty); |
| if(frag.getNextFragment() == null || frag.getNextFragment().nameIsText()) { |
| return nestedProperty; |
| } else { |
| nestedProperty.setType(JsonType.OBJECT); |
| |
| currentProperties = nestedProperty.getProperties(); |
| } |
| frag = frag.getNextFragment(); |
| propertyName = getNameForFragment(frag); |
| } |
| return null; |
| } |
| |
| private Property[] getXopIncludeProperties() { |
| if(this.xopIncludeProp == null) { |
| this.initXopIncludeProp(); |
| } |
| return this.xopIncludeProp; |
| } |
| |
| private boolean isCollection(Class<?> type) { |
| if (CoreClassConstants.Collection_Class.isAssignableFrom(type) |
| || CoreClassConstants.List_Class.isAssignableFrom(type) |
| || CoreClassConstants.Set_Class.isAssignableFrom(type)) { |
| return true; |
| } |
| return false; |
| } |
| |
| private List<ClassDescriptor> getAllDescriptorsForInheritance(XMLDescriptor descriptor) { |
| ArrayList<ClassDescriptor> descriptors = new ArrayList<ClassDescriptor>(); |
| QNameInheritancePolicy policy = (QNameInheritancePolicy) descriptor.getInheritancePolicy(); |
| descriptors.add(descriptor); |
| descriptors.addAll(policy.getAllChildDescriptors()); |
| ClassDescriptor parent = policy.getParentDescriptor(); |
| while(parent != null) { |
| descriptors.add(parent); |
| parent = parent.getInheritancePolicy().getParentDescriptor(); |
| } |
| return descriptors; |
| |
| } |
| |
| } |