All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.reprezen.genflow.rapidml.xsd.ResourceApiSchemaGenerator.xtend Maven / Gradle / Ivy

/*******************************************************************************
 * Copyright © 2013, 2016 Modelsolv, Inc.
 * All Rights Reserved.
 * 
 * NOTICE: All information contained herein is, and remains the property
 * of ModelSolv, Inc. See the file license.html in the root directory of
 * this project for further information.
 *******************************************************************************/
package com.reprezen.genflow.rapidml.xsd

import com.google.common.collect.Iterables
import com.google.common.collect.Lists
import com.reprezen.genflow.api.template.IGenTemplateContext
import com.reprezen.genflow.api.zenmodel.ZenModelExtractOutputItem
import com.reprezen.genflow.api.zenmodel.ZenModelLocator
import com.reprezen.rapidml.CollectionResource
import com.reprezen.rapidml.Feature
import com.reprezen.rapidml.Method
import com.reprezen.rapidml.ObjectRealization
import com.reprezen.rapidml.ObjectResource
import com.reprezen.rapidml.PrimitiveProperty
import com.reprezen.rapidml.PropertyRealization
import com.reprezen.rapidml.ReferenceElement
import com.reprezen.rapidml.ReferenceEmbed
import com.reprezen.rapidml.ReferenceLink
import com.reprezen.rapidml.ReferenceProperty
import com.reprezen.rapidml.ReferenceTreatment
import com.reprezen.rapidml.ResourceAPI
import com.reprezen.rapidml.ResourceDefinition
import com.reprezen.rapidml.ServiceDataResource
import com.reprezen.rapidml.Structure
import com.reprezen.rapidml.TypedMessage
import com.reprezen.rapidml.ZenModel
import com.reprezen.rapidml.util.ResourceFinder
import java.util.Collection
import java.util.List
import org.eclipse.emf.ecore.EObject

class ResourceApiSchemaGenerator extends ZenModelExtractOutputItem {
	extension FeatureHelper featureHelper
	extension ReferenceLinkHelper referenceLinkHelper
	extension XMLSchemaHelper xmlSchemaHelper
	extension ParamsHelper paramsHelper
	extension TraceHelper traceHelper
	var ZenModelLocator locator

	var ResourceFinder resourceFinder

	override void init(IGenTemplateContext context) {
		super.init(context)
	}

	override String generate(ZenModel zenModel, ResourceAPI api) {
		val helpers = new Helpers(context, zenModel)
		this.featureHelper = helpers.featureHelper
		this.referenceLinkHelper = helpers.referecneLinkHelper
		this.xmlSchemaHelper = helpers.xmlSchemaHelper
		this.paramsHelper = helpers.paramsHelper
		this.traceHelper = helpers.traceHelper

		this.locator = new ZenModelLocator(zenModel)
		this.resourceFinder = new ResourceFinder(api)
		api.traceForApi //
		.withProperty('namespace', api.namespace) //
		.withProperty('namespacePrefix', api.nsPrefix) //
		.withPrimarySourceItem(locator.locate(api)) //
		'''
			
			«api.generateSchemaImports»
			«FOR dm : api.usedDataModels SEPARATOR ""»
				
			«ENDFOR»
			
			«FOR resource : api.ownedResourceDefinitions SEPARATOR ""»
				«generateForResource(resource as ServiceDataResource, api)»
			«ENDFOR»
			
			«FOR resource : api.ownedResourceDefinitions SEPARATOR ""»
				«FOR method : resource.methods SEPARATOR ""»
					««« Make sure each method has an id value in trace
					«{method.setIdInTrace; null}»
					«method.request?.generateForMessage»
					«FOR response : method.responses SEPARATOR ""»
						«response.generateForMessage»
					«ENDFOR»
				«ENDFOR»
			«ENDFOR»
			«FOR dataType : api.zenModel.dataModels.map[it.ownedDataTypes.filter(Structure)].flatten SEPARATOR ""»
				«dataType.generateForStructure(api)»
			«ENDFOR»
			
		'''
	}

	def private generateNamespaceAdditions(ResourceAPI api) {
		'xmlns:atom="http://www.w3.org/2005/Atom"'
	}

	def private String generateSchemaImports(ResourceAPI api) {
		api.generateAtomImports
	}

//
// Per-resource generation
//
	def private generateForResource(ServiceDataResource resource, ResourceAPI api) {
		resource.traceForComplexType //
		.withProperty('complexType', resource.typeName) //
		.withProperty('elementName', resource.rootElementName) //
		.withProperty('namespace', api.namespace) //
		.withProperty('namespacePrefix', api.nsPrefix) //
		.withPrimarySourceItem(locator.locate(api)) //
		.withPrimarySourceItem(locator.locate(resource))
		'''
			«resource.dataType.generateResourceComplexType(resource, api)»
			«resource.generateResourceElement»
			«resource.generateTransitionalContainersForReferenceLinks(api)»
			«generateContainersForReferenceEmbeds(resource.name, resource, resource.referenceTreatments, api)»
		'''
	}

	def private dispatch generateResourceComplexType(Structure complexType, CollectionResource resource,
		ResourceAPI api) {
		val elementProperties = Iterables.concat(
			resource.referenceLinks,
			resource.referenceEmbeds
		)
		'''
			
			«complexType.generateXSDDoc»
			«elementProperties.generateAllBlock(resource, api)»
			
		'''
	}

	def private dispatch generateResourceComplexType(Structure complexType, ObjectResource resource, ResourceAPI api) {
		val elementProperties = Iterables.concat(
			resource.includedProperties.filterForElements,
			resource.referenceLinks,
			resource.referenceEmbeds
		)
		val attributeProperties = resource.includedProperties.filterForAttributes
		'''
			
				«complexType.generateXSDDoc»
				«elementProperties.generateAllBlock(resource, api)»
				«attributeProperties.generateAttributeDecls(api)»
			
		'''
	}

	def private generateAllBlock(Iterable properties, EObject context, ResourceAPI api) {
		generateAllBlock(properties, context, api, null)
	}

	def private generateAllBlock(Iterable properties, EObject context, ResourceAPI api,
		(EObject)=>String special) {
		if (!properties.empty) {
			'''
				
				«FOR prop : properties SEPARATOR ""»
					«special?.apply(prop) ?: generatePropertyDecl(prop, context, api, true)»
				«ENDFOR»
				
			'''
		}
	}

	def private generateResourceElement(ServiceDataResource resource) {
		'''
			
		'''
	}

	def private generateTransitionalContainersForReferenceLinks(ServiceDataResource resource, ResourceAPI api) {
		val links = resource.referenceLinks.map[it as ReferenceTreatment]
		'''
			«FOR referenceProperty : getContainmentReferencesAtPosition(links, 1)»
				«generateContainmentSegment(referenceProperty, resource, api)»
			«ENDFOR»
		'''
	}

	def private generateContainmentSegment(ReferenceProperty referenceProperty, ServiceDataResource resource,
		ResourceAPI api) {
		generateContainmentSegment(#[referenceProperty], resource, api)
	}

	def private String generateContainmentSegment(List path, ServiceDataResource resource,
		ResourceAPI api) {
		val currentType = path.findLast[].type
		val levelOfContainment = path.length
		val elementProperties = Iterables.concat(
			currentType.ownedFeatures.filterForElements,
			resource.referenceLinks.filter[startsWithPath(path)].filter[containmentDepth < levelOfContainment + 1]
		)
		val attributeProperties = currentType.ownedFeatures.filterForAttributes
		val nextLevelReferences = resource.referenceLinks.map[it as ReferenceTreatment]
		'''
			
				«elementProperties.generateAllBlock(resource, api)[
					switch it {
						ReferenceProperty:
							it.generateReferenceProperty(resource, api, path)
						default:
							null
					}
				]»
				«attributeProperties.generateAttributeDecls(api)»
			
			«FOR referenceProperty : getContainmentReferencesAtPosition(nextLevelReferences, levelOfContainment + 1)»
				«generateContainmentSegment(path.concat(referenceProperty), resource, api)»
			«ENDFOR»
		'''
	}

//
// Per-Message Generation
//
	def private generateForMessage(TypedMessage message) {
		if (message.actualType !== null) {
			val api = message.resourceAPI
			'''
				«message.actualType.generateMessageComplexType(message, api)»
				«message.generateMessageTypeElement»
				«message.generateTransitionalContainersForReferenceLinks(api)»
				«generateContainersForReferenceEmbeds(message.typeName, message, message.referenceTreatments, api)»
			'''
		}
	}

	def private generateMessageComplexType(Structure complexType, TypedMessage message, ResourceAPI api) {
		val elementProperties = Iterables.concat(
			message.includedProperties.filterForElements,
			message.referenceLinks,
			message.referenceEmbeds
		)
		val attributeProperties = message.includedProperties.filterForAttributes
		'''
			
			«complexType.generateXSDDoc»
			«elementProperties.generateAllBlock(message, api)[
				switch it {
					ReferenceEmbed: {
						val nextReference = it.referenceElement as ReferenceProperty
						nextReference.generateNestedReference(extend(message.typeName, #[nextReference]))
					}
					default:
						null
				}
			]»
			«attributeProperties.generateAttributeDecls(api)»
			
		'''
	}

	def private generateMessageTypeElement(TypedMessage message) {
		'''
			
		'''
	}

	def private generateTransitionalContainersForReferenceLinks(TypedMessage message,
		ResourceAPI api) {
		'''
			«FOR referenceProperty : getContainmentReferencesAtPosition(message.referenceLinks.map[it as ReferenceTreatment], 1)»
				«generateContainmentSegment(referenceProperty, message, api)»
			«ENDFOR»
		'''
	}

	def private generateContainmentSegment(ReferenceProperty referenceProperty, TypedMessage message, ResourceAPI api) {
		generateContainmentSegment(#[referenceProperty], message, api)
	}

	def private String generateContainmentSegment(List path, TypedMessage message, ResourceAPI api) {
		val currentType = path.findLast[].type;
		val levelOfContainment = path.length
		val elementProperties = Iterables.concat(
			currentType.ownedFeatures.filterForElements,
			message.referenceLinks.filter[startsWithPath(path)].filter[containmentDepth < levelOfContainment + 1]
		)
		val attributeProperties = currentType.ownedFeatures.
			filterForAttributes
		'''
			
			«elementProperties.generateAllBlock(message, api)»
			«attributeProperties.generateAttributeDecls(api)»
			
			«FOR referenceProperty : getContainmentReferencesAtPosition(message.referenceLinks.map[it as ReferenceTreatment], levelOfContainment + 1)»
				«generateContainmentSegment(path.concat(referenceProperty), message, api)»
			«ENDFOR»
		'''
	}

//
// ReferenceEmbed generation - for both Resources and Messages
//
	def private generateContainersForReferenceEmbeds(String name, EObject obj,
		Iterable referenceTreatments, ResourceAPI api) {
		'''
			«FOR referenceEmbed : referenceTreatments.filter(ReferenceEmbed)»
				«referenceEmbed.generateContainersForReferenceEmbed(name, obj, api)»
			«ENDFOR»
		'''
	}

	def private String generateContainersForReferenceEmbed(ReferenceEmbed referenceEmbed, String name, EObject obj,
		ResourceAPI api) {
		val path = getPathTo(referenceEmbed)
		'''
			«path.generateEmbedSegment(name, obj, referenceEmbed)»
			«FOR childReferenceEmbed : referenceEmbed.nestedReferenceTreatments.filter(ReferenceEmbed)»
				«childReferenceEmbed.generateContainersForReferenceEmbed(name, obj, api)»
			«ENDFOR»
		'''
	}

	def private List getPathTo(ReferenceEmbed referenceEmbed) {
		val Collection containers = Lists.newArrayList
		val Collection references = Lists.newArrayList
		references.addAll(referenceEmbed.containmentReferences)
		if (referenceEmbed.eContainer.eContainer instanceof ReferenceEmbed) {
			containers.addAll(getPathTo(referenceEmbed.eContainer.eContainer as ReferenceEmbed))
		}
		references.add(referenceEmbed.referenceElement as ReferenceProperty)
		Iterables.concat(containers, references).toList
	}

	def private generateEmbedSegment(List path, String baseName, EObject obj,
		ReferenceEmbed referenceEmbed) {
		val elementProperties = Iterables.concat(
			referenceEmbed.linkDescriptor.allIncludedProperties.map[baseProperty].filterForElements,
			referenceEmbed.nestedReferenceTreatments.filter(ReferenceLink),
			referenceEmbed.nestedReferenceTreatments.filter(ReferenceEmbed)
		)
		val attributeProperties = referenceEmbed.linkDescriptor.allIncludedProperties.map[baseProperty].
			filterForAttributes
		'''
			
			«IF referenceEmbed.linkDescriptor !== null»
				«elementProperties.generateAllBlock(obj, obj.resourceAPI)[
					switch it {
						ReferenceEmbed: {
							val nextSegment = it.referenceElement as ReferenceProperty
							nextSegment.generateNestedReference(extend(baseName, path.concat(nextSegment)))
						}
						default:
							null							
					}
				]»
				«attributeProperties.generateAttributeDecls(obj.resourceAPI)»
			«ENDIF»
			
		'''
	}

//
// Generators for properties in within complex types
//
	def private dispatch String generatePropertyDecl(EObject prop, EObject context, ResourceAPI api,
		boolean asElement) {
		throw new IllegalArgumentException('''Cannot gernerate property decl for «prop?.class» in context «context?.class»''')
	}

	def private dispatch String generatePropertyDecl(PrimitiveProperty prop, EObject context, ResourceAPI api,
		boolean asElement) {
		if (asElement) {
			if (prop.
				isMultiValued) {
				'''
					
						
							
								
							
						
					
				'''
			} else {
				''''''
			}
		} else {
			if (prop.isMultiValued) {
				throw new IllegalArgumentException("Cannot declare multi-valued property as a schema attribute");
			}
			'''
				
			'''
		}
	}

	def private dispatch String generatePropertyDecl(PropertyRealization property, EObject context, ResourceAPI api,
		boolean asElement) {
		switch (property.baseProperty) {
			PrimitiveProperty:
				if (asElement) {
					if (property.
						isMultiValued) {
						'''
							
								
									
										
									
								
							
						'''
					} else {
						''''''
					}
				} else {
					if (property.isMultiValued) {
						throw new IllegalArgumentException("Cannot declare multi-valued property as a schema attribute")
					}
					'''
						
						«IF !property.allConstraints.nullOrEmpty»
							
								«property.basePrimitiveProperty.getTypeName(api).generateRestriction(property.allConstraints)»
							
						«ENDIF»
						
					'''
				}
			ReferenceProperty:
				property.generateReferenceProperty(context, api, #[])
		}
	}

	def private dispatch String generatePropertyDecl(ReferenceLink link, EObject context, ResourceAPI api,
		boolean asElement) {
		val ReferenceElement referenceProperty = link.referenceProperty
		val ObjectRealization linkDescriptor = link.linkDescriptor
		val elementProperties = Iterables.concat(
			#[link],
			linkDescriptor?.allIncludedProperties?.map[baseProperty]?.primitiveProperties?.filterForElements ?: #[]
		)
		val attributeProperties = linkDescriptor?.allIncludedProperties?.map[baseProperty]?.primitiveProperties?.
			filterForAttributes ?: #[]
		if (referenceProperty.
			isMultiValued) {
			'''
				
					
						
							
								
										«elementProperties.generateAllBlock(context, api) [
										switch it {
											ReferenceLink: it.generateAtomLink
											default: null
										}
									]»
									«attributeProperties.generateAttributeDecls(api)»
								
							
						
					
				
			'''

		} else {
			'''
				
					
						«elementProperties.generateAllBlock(context, api)[
							switch it {
								ReferenceLink: it.generateAtomLink
								default: null
							}
						]»
						«attributeProperties.generateAttributeDecls(api)»
					
				
			'''
		}
	}

	def private dispatch String generatePropertyDecl(ReferenceEmbed embed, EObject context, ResourceAPI api,
		boolean asElement) {
		val baseName = switch context {
			TypedMessage: context.typeName
			ServiceDataResource: context.name
		}
		val complexTypeName = baseName.extend(#[embed.referenceElement])
		generateNestedReference(embed.referenceElement, complexTypeName)
	}

	def private dispatch String generatePropertyDecl(ReferenceProperty prop, EObject context, ResourceAPI api,
		boolean asElement) {
		prop.generateReferenceProperty(context, api, #[])
	}

	def private dispatch String generateReferenceProperty(PropertyRealization referenceProperty,
		ServiceDataResource resource, ResourceAPI api, List path) {
		if (referenceProperty.baseReferenceProperty.isPropertyOverridenByReferenceLink(resource, path)) {
			referenceProperty.generateContainmentProperty(
				resource.typeName.extend(path.concat(referenceProperty.baseReferenceProperty)))
		}
	}

	def private dispatch String generateReferenceProperty(ReferenceProperty referenceProperty,
		ServiceDataResource resource, ResourceAPI api, List path) {
		if (isPropertyOverridenByReferenceLink(referenceProperty, resource, path)) {
			referenceProperty.generateNestedReference(resource.typeName.extend(path.concat(referenceProperty)))
		}
	}

	def private dispatch generateReferenceProperty(ReferenceProperty referenceProperty, TypedMessage message,
		ResourceAPI api, List path) {
		if (isPropertyOverridenByReferenceLink(referenceProperty, message, path)) {
			referenceProperty.generateNestedReference(message.typeName.extend(path.concat(referenceProperty)))
		}
	}

	//
	// Nested references
	//
	def private String generateNestedReference(ReferenceElement referenceElement, String typeName) {
		if (!referenceElement.isMultiValued)
			referenceElement.generateSimpleNestedReference(typeName)
		else
			referenceElement.generateMultiNestedReference(typeName)
	}

	def private String generateSimpleNestedReference(ReferenceElement referenceElement,
		String typeName) {
		'''
			
		'''
	}

	def private String generateMultiNestedReference(ReferenceElement referenceElement,
		String typeName) {
		'''
			
				
					
						
					
				
			
		'''
	}

	//
	// Containment
	//
	def private String generateContainmentProperty(PropertyRealization property, String typeName) {
		if (!property.isMultiValued) {
			property.generateSimpleContainmentProperty(typeName)
		} else {
			property.generateMultiContainmentProperty(typeName)
		}
	}

	def private String generateSimpleContainmentProperty(PropertyRealization property,
		String typeName) {
		'''
			
		'''
	}

	def private String generateMultiContainmentProperty(PropertyRealization property,
		String typeName) {
		'''
			
				
					
						
					
				
			
		'''
	}

	//
	// Miscellaneous
	//
	def private String generateAtomImports(ResourceAPI api) {
		'''
			
		'''
	}

	def private String generateAtomLink(ReferenceLink referenceLink) {
		if (referenceLink.targetResource !== null) '''
				
				
				
			'''
	}

	def private dispatch String generateReferenceProperty(PropertyRealization referenceProperty, TypedMessage message,
		ResourceAPI api, List path) {
		if (referenceProperty.baseReferenceProperty.isPropertyOverridenByReferenceLink(message, path)) {
			generateContainmentProperty(referenceProperty,
				message.typeName.extend(path.concat(referenceProperty.baseReferenceProperty)))
		}
	}

	def private getPropertyUse(PropertyRealization property) {
		if(property.isRequired) 'required' else 'optional'
	}

	def private generateForStructure(Structure structure, ResourceAPI api) {
		if (resourceFinder.findResource(structure, true) === null) {
			val elementProperties = structure.ownedFeatures.filter[isPrimitiveProperty].filterForElements
			val attrProperties = structure.ownedFeatures.filterForAttributes
			'''
				
					«structure.generateXSDDoc»
					«elementProperties.generateAllBlock(structure, api)»
					«attrProperties.generateAttributeDecls(api)»
				
			'''
		}
	}

	def private dispatch String generateAttributeDecls(ReferenceEmbed embed, ResourceAPI api) {
		embed.linkDescriptor.generateAttributeDecls(api);
	}

	def private dispatch String generateAttributeDecls(ObjectRealization objectRealization, ResourceAPI api) {
		objectRealization.allIncludedProperties.primitiveSingleProperties.generateAttributeDecls(api)
	}

	def private dispatch String generateAttributeDecls(Iterable properties, ResourceAPI api) {
		'''
			«FOR property : properties SEPARATOR ""»
				«property.generatePropertyDecl(api, api, false)»
			«ENDFOR»
		'''
	}

	def private ReferenceProperty getBaseReferenceProperty(PropertyRealization property) {
		property.baseProperty as ReferenceProperty
	}

	def private PrimitiveProperty getBasePrimitiveProperty(PropertyRealization property) {
		property.baseProperty as PrimitiveProperty
	}

	def private dispatch ResourceAPI getResourceAPI(TypedMessage message) {
		message.getEContainer(Method).containingResourceDefinition.resourceAPI
	}

	def private dispatch ResourceAPI getResourceAPI(ResourceDefinition resource) {
		resource.eContainer as ResourceAPI
	}

	def private  List concat(List list, T value) {
		val copy = Lists.newArrayList(list)
		copy.add(value)
		return copy
	}

	def private  Iterable filterForElements(Iterable properties) {
		properties.filter[eltStyle || !it.isAttributeEligible].map[it as EObject]
	}

	def private  Iterable filterForAttributes(Iterable properties) {
		properties.filter[attrStyle && it.isAttributeEligible].map[it as EObject]
	}

	def private boolean isAttributeEligible(EObject property) {
		switch property {
			Feature: property.isPrimitiveProperty && property.isSingleValued
			PropertyRealization: property.baseProperty.isPrimitiveProperty && property.isSingleValued
			default: throw new IllegalArgumentException
		}
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy