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

org.joinedworkz.common.helper.CmnModelHelper.xtend Maven / Gradle / Ivy

There is a newer version: 1.3.46
Show newest version
package org.joinedworkz.common.helper

import java.util.Collection
import java.util.Collections
import java.util.List
import java.util.Set
import javax.inject.Inject
import javax.inject.Singleton
import org.joinedworkz.common.adapter.KeyAdapter
import org.joinedworkz.common.info.DataInfo
import org.joinedworkz.common.strategies.ColumnNameStrategy
import org.joinedworkz.common.strategies.DtoFieldNameStrategy
import org.joinedworkz.common.strategies.TableNameStrategy
import org.joinedworkz.core.model.CmnCardinality
import org.joinedworkz.core.model.CmnComplexType
import org.joinedworkz.core.model.CmnElement
import org.joinedworkz.core.model.CmnEnumeration
import org.joinedworkz.core.model.CmnField
import org.joinedworkz.core.model.CmnFieldContainer
import org.joinedworkz.core.model.CmnFieldSet
import org.joinedworkz.core.model.CmnModel
import org.joinedworkz.core.model.CmnNamedObject
import org.joinedworkz.core.model.CmnObject
import org.joinedworkz.core.model.CmnOperation
import org.joinedworkz.core.model.CmnOperationContainer
import org.joinedworkz.core.model.CmnOperationParameter
import org.joinedworkz.core.model.CmnPlatform
import org.joinedworkz.core.model.CmnPropertyDefinition
import org.joinedworkz.core.model.CmnResource
import org.joinedworkz.core.model.CmnService
import org.joinedworkz.core.model.CmnType
import org.joinedworkz.core.model.data.CmnData
import org.joinedworkz.core.model.data.CmnPathCallSegment
import org.joinedworkz.core.model.data.CmnReferenceData
import org.joinedworkz.core.model.data.CmnResourceData
import org.joinedworkz.core.model.CmnProperty

@Singleton
class CmnModelHelper {
    
    @Inject
    TableNameStrategy tableNameStrategy
    
    @Inject
    DtoFieldNameStrategy dtoNameStrategy
    
    @Inject
    ColumnNameStrategy columnNameStrategy
	
	val static char BRACKET = '('
	val static char DOT = '.'
	
	protected extension NameHelper nameHelper = new NameHelper
	protected extension StringHelper stringHelper = new StringHelper
	
	final protected static String JAVA_NAME = "javaName";
	final protected static String JAVA_TYPE = "javaType";
	final protected static String TABLE_NAME = "tableName";
	final protected static String COLUMN_NAME = "columnName";
	final protected static String COLUMN_TYPE = "columnType";
	
	def String lastSegment(String type) {
		if (type !== null) {
			val bracketIndex = type.indexOf(BRACKET)
			var relevantDotIndex = -1
			if (bracketIndex > 0) {
				val beforeBracket = type.substring(0,bracketIndex)
				relevantDotIndex = beforeBracket.lastIndexOf(DOT)
			} else {
				relevantDotIndex = type.lastIndexOf(DOT)
			}
			if (relevantDotIndex > 0) {
				return type.substring(relevantDotIndex + 1)
			}
		}
		return type
	}
	
	def classPrefixForNamespace(CmnModel model) {
	    if (model !== null) {
            val segments = model.namespace.split('\\.')
            val segmentCount = segments.size
            if (segmentCount > 2) {
                return segments.get(segmentCount-2).toFirstUpper + segments.get(segmentCount-1).toFirstUpper
            } else if (segmentCount == 2) {
                return  segments.get(segmentCount-1).toFirstUpper
            } else {
                return model.namespace
            }
	    } else {
	        return "UNDEFINED"
	    }
    }
	
	def String packageSegments(String type) {
		if (type !== null) {
			val lastDotIndex = type.lastIndexOf('.')
			if (lastDotIndex > 0) {
				return type.substring(0,lastDotIndex)
			}
		}
		return type
	}
	
	def CmnModel getModel(CmnElement cmnObject) {
		if (cmnObject instanceof CmnModel) {
			return cmnObject
		} else if (cmnObject.container !== null) {
			return getModel(cmnObject.container)
		} else {
		    throw new RuntimeException('Model not found for element of type ' + cmnObject?.class.canonicalName)
		}
	}
	
    def concreteFieldContainers(CmnModel model) {
        model.modelElements.filter(e | isDtoObject(e)).filter(CmnFieldContainer).filter[isConcrete]
    }
    
	def boolean isDtoObject(CmnObject it) {
		return it instanceof CmnFieldContainer && !(it instanceof CmnFieldSet)
	}
	
	def isConcrete(CmnFieldContainer it) {
        abstract === null || !abstract
    }
    
    def isFinalized(CmnObject it) {
        model.isTrue('finalized')
    }
    
    def isAvailableInPlatform(CmnObject it, String qualifiedPlatformName) {
        val modelOfObject = model
        if (modelOfObject.isTrue('finalized')) {
            return modelOfObject.platform.isPlatform(qualifiedPlatformName)
        }
        return true        
    }
    
    def boolean isPlatform(CmnPlatform platform, String qualifiedPlatformName) {
        if (platform.qualifiedName == qualifiedPlatformName) {
            return true
        } else if (platform.basePlatform !== null) {
            return isPlatform(platform.basePlatform, qualifiedPlatformName)
        } else {
            return false
        }
    }
	
	def boolean multipleOccurences(Integer maxOccurs) {
        return maxOccurs !== null && (1 !== maxOccurs)
    }
	
	def CmnFieldContainer getBaseFieldContainer(CmnElement it) {
        if (it instanceof CmnType) {
            val baseType = it.baseType
            if (baseType instanceof CmnComplexType) {
                return baseType
            }
        }
        return null
    }
    
    def Set collectBaseTypes(Set types) {
		var collectedTypes = newLinkedHashSet
		for (type : types) {
			addBaseTypeRecursive(collectedTypes, type)
		}
		return collectedTypes
	}
	
	def void addBaseTypeRecursive(Set collectedTypes, CmnType type) {
		val baseType = type.baseType
		if (baseType !== null) {
			collectedTypes.add(baseType)
			addBaseTypeRecursive(collectedTypes, baseType)
		}
	}
	
	def determineMappedSourceTypes(CmnComplexType dtoType) {
        val baseTypes = newLinkedHashSet
        val sourceTypes = dtoType.fields.filter[sourceField !== null].map[sourceField.container].filter(CmnComplexType).toSet
        for (sourceType : sourceTypes) {
            val baseType = sourceType.baseType
            if (baseType !== null && sourceTypes.contains(baseType)) {
                baseTypes.add(baseType)
            }
        }
        sourceTypes.removeAll(baseTypes)
        return sourceTypes
    }
	
	def String getName(CmnElement cmnObject) {
        if (cmnObject instanceof CmnNamedObject) {
            return cmnObject.name
        }
	}
	
	def String getName(CmnOperationParameter it) {
        if (name !== null) {
            return name
        } else if (referencedField !== null) {
        	return referencedField.getName()
        }
	}
		
	def qualifiedName(CmnNamedObject it) {
		return model.namespace+'.'+name
	}
	
	def Iterable getPropertyValues(CmnObject object, String propertyName) {

        val propertyValue = getPropertyValue(object, propertyName)
        if (propertyValue === null) {
            return emptySet
        } else if (propertyValue instanceof Collection) {
            return propertyValue
        } else {
            return Collections.singleton(propertyValue)
        }
    }

    def  getPropertyValue(CmnObject it, String propertyName) {
        val property = getProperty(propertyName)
        if (property !== null) {
            return property.getValue() as T
        }
    }
	
	def Integer getInteger(CmnObject object, String propertyName) {
	     val property = object.getProperty(propertyName)
	     if (property !== null) {
	        val propertyValue = property.value
	        if (propertyValue instanceof Integer) {
	            return propertyValue as Integer
	        }
	     }
	}
	
	def Boolean getBoolean(CmnObject object, String propertyName) {
         val property = object.getProperty(propertyName)
         if (property !== null) {
            val propertyValue = property.value
            if (propertyValue instanceof Boolean) {
                return propertyValue as Boolean
            }
         }
    }
    
     def boolean isTrue(CmnObject object, String propertyName) {
         val property = object.getProperty(propertyName)
         return property.isTrue()
    }
    
    def boolean isTrue(CmnProperty property) {
        if (property !== null) {
            val propertyValue = property.value
            if (propertyValue instanceof Boolean) {
                return (propertyValue as Boolean).booleanValue
            }
        }
        return false
    }
    
    def boolean isFalse(CmnObject object, String propertyName) {
         val property = object.getProperty(propertyName)
         return property.isFalse()
    }
    
     def boolean isFalse(CmnProperty property) {
        if (property !== null) {
            val propertyValue = property.value
            if (propertyValue instanceof Boolean) {
                return !(propertyValue as Boolean).booleanValue
            }
        }
        return false
    }

    def titleFromDescription(String description) {
		val titleLine = textInLineAfterMarker(description, '@title')
		if (titleLine !== null) {
			return titleLine.trim
		}
	}
	
	def descriptionText(String description) {
		if (description !== null) {
			val lastAnnotationIndex = description.lastIndexOf('@')
			if (lastAnnotationIndex == -1) {
				return description
			} else {
				val textBeginIndex = indexOfLineBreakAfter(description, lastAnnotationIndex)
				if (textBeginIndex >= 0) {
					return description.substring(textBeginIndex).trim()
				}
			}
		}
		
	}
        
    def determineEnumType(CmnType type) {
        if (type instanceof CmnEnumeration) {
            return type.hasStereoType('integer') ? 'Integer' : null
        }
    }
	
	def String javaType(CmnElement object) {
	    if (object !== null) {
    		val javaType = object.javaTypeProperty
    		if (javaType !== null) {
    			return javaType
    		}
    		
    		if (object instanceof CmnField) {
    			return object.type?.name
    		} else if (object instanceof CmnType) {
    			return object.name
    		}
	    }
	}
	
	def javaTypeProperty(CmnElement it) {
		getString(JAVA_TYPE)
	}
	
	def CmnType keyType(CmnType it) {
		if (it instanceof CmnFieldContainer) {
			return keyFieldOfType?.type
		}
	}
    
    def String keyJavaName(CmnType it) {
		if (it instanceof CmnFieldContainer) {
			return keyFieldOfType?.javaName
		}
	}
	
	def CmnField keyFieldOfType(CmnType it) { 
		return keyFieldOfType(true)
	}
	
	def CmnField keyFieldOfType(CmnType it, boolean throwExceptionIfNoKeyField) {  
		if (it instanceof CmnFieldContainer) {
		 	var keyAdapter = getAdapter(KeyAdapter) // cache key field for containing type (the referenced type)
		 	if (keyAdapter === null) {
			 	var keyField = keyFields?.head
				if (keyField === null) {
					keyField = baseType?.keyFieldOfType(throwExceptionIfNoKeyField)
				}
				if (throwExceptionIfNoKeyField && keyField === null) {
					System.err.println("Key field not found of type: " + name)
					System.err.println("Key fields size: " + keyFields.size)
					throw new IllegalStateException("No key fields found for type: " + name)
				}
				keyAdapter = new KeyAdapter(keyField)
				putAdapter(keyAdapter)
			}
			return keyAdapter.keyField
		}
	}
	
	def isKey(CmnField it) {
		if (container instanceof CmnFieldContainer) {
			return (container as CmnFieldContainer).containsKeyField(it)
		} else {
			hasStereoType('key') // TODO remove this deprecated variant
		}
	}
	
	def boolean containsKeyField(CmnFieldContainer container, CmnField field) {
		val containsKey = container.keyFields?.contains(field)
		if (containsKey) {
			return true
		} else if (container.container instanceof CmnFieldContainer) {
			val containerContainsKey = (container.container as CmnFieldContainer).containsKeyField(field)
			if (containsKey) {
				return true
			}
		} 
		return field.hasStereoType('key')  // TODO remove this deprecated variant
	}
		
	def String javaType(CmnOperationParameter object) {
        if (object.type !== null) {
            return object.type.javaType
        } else if (object.referencedField !== null) {
            return object.referencedField.javaDtoFieldType
        }
    }
	
	def CmnType dtoFieldType(CmnField field) {
    	if (field.isReferenceField) {
    	    if (field.isRelation !== null && field.isRelation.booleanValue) {
    	        val relatedContainer = field.sourceField.container
    	        if (relatedContainer instanceof CmnType) {
    	            return relatedContainer.keyType
    	        }
    	    }
    		val keyFieldType = field.referencedRootField.type.keyType
    		if (keyFieldType === null) {
    			System.err.println("Error: key type not found for field " + field.name + ' of type ' + field.type.name)
    		}
    		return keyFieldType
    	} else {
    		return field.type 
    	}
    }
    
    def CmnType typeOfField(CmnField field) {
        if (field.type !== null) {
            return field.type
        } else if (field.sourceField !== null) {
            return typeOfField(field.sourceField)
        }
    }
    
    def CmnField referencedRootField(CmnField field) {
    	if (field.sourceField !== null  && !field.relationField) {
    		System.out.println("Get root of: "+field.container.name +"."+field.name)
    		return field.sourceField.referencedRootField
    	} else {
    		return field
    	}
    }
    
    def Iterable collectAllReferences(CmnFieldContainer it, List references) {
        references.addAll(fields.filter[isReferenceField])
        val base = baseFieldContainer
        if (base !== null) {
            base.collectAllReferences(references)
        }
        return references
    }
    
    def Iterable collectReferencesToBeImplemented(CmnFieldContainer it, List references) {
        references.addAll(fields.filter[isReferenceField])
        val base = baseFieldContainer
        if (base !== null && base.abstract) {
            base.collectReferencesToBeImplemented(references)
        }
        return references
    }
    
    def boolean isReferenceField(CmnField field) {
    	if (Boolean.TRUE === field.isReference) {
    		return true
    	} else if (field.sourceField !== null && !field.relationField) {
    		val processedFields = newHashSet
    		processedFields.add(field)
    		return field.sourceField.isReferenceFieldRecursive(processedFields)
    	}
    	return false
    }
    
    def boolean isReferenceFieldRecursive(CmnField field, Set processedFields) {
    	if (processedFields.contains(field)) {
    		System.out.println("Field already processed: " + field.name)
    		return false
    	}
    	processedFields.add(field)
    	if (Boolean.TRUE === field.isReference) {
    		return true
    	} else if (field.sourceField !== null) {
			if (processedFields.contains(field.sourceField)) {
	    		System.out.println("Source field already processed: " + field.sourceField.name)
	    		return false
	    	}
    		return field.sourceField.isReferenceFieldRecursive(processedFields)
    	}
    	return false
    }
	
	def String javaDtoFieldType(CmnField field) {
		if (field !== null) {
			if (!field.isReferenceField) {  // ignore type property in case of reference (then the type property of the key-field is relevant)
				val typeFromProperty = field.javaTypeProperty
				if (typeFromProperty !== null) {
					return typeFromProperty
				}
			}
			val dtoType = field.dtoFieldType
			if (dtoType !== null) {
				return dtoType.javaType
			} else {
				return 'String'
			}
		} else {
			return null
		}
    }
    
    def String javaDtoFieldName(CmnField field) {
    	return dtoNameStrategy.apply(field)
	}
	
	def String sourceFieldJavaNameRecursive(CmnField field) {
		if (field.sourceField !== null) {
			return field.sourceField.sourceFieldJavaNameRecursive
		} else {
			return field.javaName
		}
	}
	
	def String javaName(CmnField field) {
		val javaName = field.getString(JAVA_NAME)
		if (javaName !== null) {
			return javaName
		} else {
			return field.name
		}
	}
	
	def String javaName(CmnOperationParameter it) {
        if (name !== null) {
            return name
        } else if (referencedField !== null) {
            return referencedField.javaDtoFieldName
        }
    }
	
	def String parameterName(CmnOperationParameter it) {
        if (name !== null) {
            return name
        } else if (referencedField !== null) {
            return referencedField.javaName
        }
    }
	
	def String tableName(CmnElement object) {
		if (object instanceof CmnFieldContainer) {
			val tableName = object.getString(TABLE_NAME)
			if (tableName !== null) {
				return tableName
			}
			// TODO instead of explicitly calling the following method invoke table name strategy on all related model elements
			return tableNameStrategy.defaultTableName(object)
		}
		return "UNDEFINED_TABLE_NAME"

	}
	
	def String columnName(CmnField field) {
	    if (field !== null) {
    		val javaName = field.getString(COLUMN_NAME)
    		if (javaName !== null) {
    			return javaName
    		} 
		    // TODO instead of explicitly calling the following method invoke column name strategy on all related model elements
			return columnNameStrategy.defaultColumnName(field)
	    }
	}
	
	def String columnType(CmnField field) {
		val columnType = field.getString(COLUMN_TYPE)
		val javaType = field.javaDtoFieldType
		if (columnType !== null) {
			return columnType
		} else if (javaType !== null) {
		    if (javaType.endsWith('UUID')) {
		        return 'UUID'
		    } else if (javaType.endsWith('Integer')) {
		        val precision = field.getInteger('precision')
		        if (precision !== null && precision < 5) {
		            return 'SMALLINT'
		        } else {
		            return 'INTEGER'
		        }
		    } else if (javaType.endsWith('Long')) {
                return 'BIGINT'
		    } else if (javaType.endsWith('String')) {
		    	return 'VARCHAR'
		    }
		}
		if (field.type instanceof CmnEnumeration) {
		    val enumType = determineEnumType(field.type)
		    if (enumType == 'Integer') {
		        return 'INTEGER'
		    } else {
		        return 'VARCHAR'
		    }
		}
		val typeName = field.type?.name?.toUpperCase
		if (hasStereoTypeString(field.type)) {
		    if ("TEXT" != typeName) {
    			return "VARCHAR"
		    }
		}
		if (field.type instanceof CmnComplexType) { 
			return 'JSON'
		}
		return typeName
		
	}
	
	def firstStereoType(CmnObject it) {
		if (stereoTypes !== null) {
			return stereoTypes.head
		}
	}
	
	def hasStereoType(CmnObject it, String stereoTypeName) {
		return stereoTypes !== null && stereoTypes.contains(stereoTypeName)
	}
	
	def hasStereoType(CmnComplexType it, String stereoTypeName) {
        return stereoTypes !== null && stereoTypes.contains(stereoTypeName)
    }
    
    def hasStereoType(CmnFieldContainer it, String stereoTypeName) {
        return stereoTypes !== null && stereoTypes.contains(stereoTypeName)
    }
    
    def hasStereoTypeContainedIn(CmnObject it, String ...stereoTypeNames) {
        if ( stereoTypes !== null) {
            for (stereoTypeName : stereoTypeNames) {
                if (stereoTypes.contains(stereoTypeName)) {
                    return true
                }
            }
        }
        return false
    }
	
	def hasStereoTypeString(CmnType it) {
		hasStereoType("string")
	}
	
	
	def Iterable allFields(CmnFieldContainer fieldContainer, boolean pullAllBaseFields) {
		if (fieldContainer instanceof CmnComplexType) {
    		val Collection allFields = newArrayList
    		collectFields(allFields, fieldContainer, pullAllBaseFields)
    		return allFields
		} else {
		    return fieldContainer.fields
		}
	}
		
	def void collectFields(Collection collectedFields, CmnType type, boolean pullAllBaseFields) {
		if (type instanceof CmnComplexType) {
		    if (pullAllBaseFields || type.hasAbstractBaseType) {
			     collectFields(collectedFields, type.baseType, pullAllBaseFields)
			}
			collectedFields.addAll(type.fields)
		}
	}
	
	def Iterable allDtoFields(CmnFieldContainer fieldContainer, boolean pullAllBaseFields) {
		if (fieldContainer instanceof CmnComplexType) {
    		val Collection allFields = newArrayList
    		collectDtoFields(allFields, fieldContainer, pullAllBaseFields)
    		return allFields
		} else {
		    return fieldContainer.dtoFields
		}
	}
		
	def void collectDtoFields(Collection collectedFields, CmnType type, boolean pullAllBaseFields) {
		if (type instanceof CmnComplexType) {
		    if (pullAllBaseFields || type.hasAbstractBaseType) {
			     collectFields(collectedFields, type.baseType, pullAllBaseFields)
			}
			collectedFields.addAll(type.dtoFields)
		}
	}
		
	def Iterable dtoFields(CmnFieldContainer it) {
	    return fields.filter[!isM2NRelation]
	}
	
	def isM2NRelation(CmnField it) {
        if (isRelationField) {
            return isCollection && sourceField !== null && sourceField.isCollection
        } else {
            return false
        }
    }
    
//    def Collection allOperations(CmnOperationContainer service, boolean pullAllBaseOperations) {
//		if (service.operations !== null) {
//			return service.operations
//		}
//		return Collections.emptyList
//	}
	
	def Collection allOperations(CmnOperationContainer operationContainer, boolean pullAllBaseOperations) {
		if (operationContainer instanceof CmnService) {
			return operationContainer.operations;
		} else {
    		val Collection allOperations = newArrayList
    		collectOperations(allOperations, operationContainer, pullAllBaseOperations)
    		return allOperations
		}
	}
		
	def void collectOperations(Collection collectedOperations, CmnElement type, boolean pullAllBaseOperations) {
		if (type instanceof CmnComplexType) {
		    if (pullAllBaseOperations || type.hasAbstractBaseType) {
		        collectOperations(collectedOperations, type.baseType, pullAllBaseOperations)
		    }
		}
		if (type instanceof CmnFieldContainer) {
			collectedOperations.addAll(type.operations)
		}
	}
	    
    def CmnType determineReturnType(CmnOperation operation) {
        if (operation.returnType !== null) {
            return operation.returnType
        } else if (operation.baseOperation !== null) {
            return determineReturnType(operation.baseOperation);
        }
    }
	
	def boolean hasAbstractBaseType(CmnComplexType it) {
	    val baseFieldContainer = getBaseFieldContainer
	    return baseFieldContainer.isAbstract
	}
	
	def boolean isAbstract(CmnFieldContainer it) {
        if (it !== null) {
            val isAbstract = it.abstract
           return isAbstract !== null && isAbstract
        } else {
            return false
        }
    }
    
    def boolean isAbstract(CmnOperation it) {
        if (it !== null) {
            val isAbstract = it.abstract
           return isAbstract !== null && isAbstract
        } else {
            return false
        }
    }
    
    def boolean isCollection(CmnCardinality it) {
    	if (it !== null) {
	    	return maxOccurs !== null && (maxOccurs < 0 || maxOccurs > 1)
    	} else {
    		return false
    	}
    }
    
    def boolean isRequired(CmnCardinality it) {
    	if (it !== null) {
	    	return minOccurs !== null && minOccurs > 0
    	} else {
    		return false
    	}
    }
    
    def boolean isCollection(CmnOperationParameter it) {
    	if (it !== null) {
	    	if (referencedField !== null) {
	    		return referencedField.isCollection || isCollection(it as CmnCardinality)
	    	} else {
	    		return isCollection(it as CmnCardinality)
	    	}
    	} else {
    		return false
    	}
    }
		
	def boolean isCollection(CmnField field) {
	    
	    val collectionIsSpecifiedOnField = field.maxOccurs !== null && (field.maxOccurs < 0 || field.maxOccurs > 1)
	    if (collectionIsSpecifiedOnField) {
	        return true
	    }
	    
        if (field.relation !== null) {

            val fieldContainer = field.container
            if (field.relation.leftType == fieldContainer) {
                return field.relation.leftMaxOccurs !== null &&
                    (field.relation.leftMaxOccurs < 0 || field.relation.leftMaxOccurs > 1)
            } else
                (field.relation.rightType == fieldContainer)
            {
                return field.relation.rightMaxOccurs !== null &&
                    (field.relation.rightMaxOccurs < 0 || field.relation.rightMaxOccurs > 1)
            }

        }
        return false

    }
    
    def isRelationField(CmnField it) {
        if (isRelation !== null && isRelation) {
            return isCollection && sourceField !== null && sourceField.isCollection
        } else {
            return false
        }
    }
    
    def List getImmutableFields(CmnFieldContainer it, boolean recursive) {
        if (it !== null) {
            val immutable = hasStereoType('immutable')
            val immutableFields = newArrayList
            if (recursive) {
                immutableFields .addAll(getImmutableFields(baseFieldContainer, true))
            }
            immutableFields.addAll(fields.filter[immutable || hasStereoType('immutable')])
            return immutableFields
        } else {
            return emptyList
        }
    }
    
    def boolean hasImmutableFields(CmnFieldContainer it, boolean recursive) {
        if (it !== null && fields !== null && !fields.empty) {
           if (hasStereoType('immutable')) {
               return true
           }
           if (fields.exists[hasStereoType('immutable')]) {
               return true
           }
           if (recursive) {
                return hasImmutableFields(baseFieldContainer, true)
           }
        }
        return false
    }
    
    def Iterable allImmutableFields(CmnFieldContainer complexType) {
        val baseImmutableFields = complexType.baseFieldContainer.getImmutableFields(true).filter[!isCollection]
        val immutableFields = complexType.getImmutableFields(false).filter[!isCollection]
        if (!immutableFields.empty || !baseImmutableFields.empty) {
            val allImmutableFields = newArrayList
            allImmutableFields.addAll(baseImmutableFields)
            allImmutableFields.addAll(immutableFields)
            return allImmutableFields
        }
        return emptyList
    }
    
    def CmnResource lastResourceOfPath(List resourcePath) {
    	if (resourcePath !== null) {
    		for (var i=resourcePath.length-1;i>=0;i--) {
    			val segment = resourcePath.get(i)
    			if (segment.resource !== null) {
    				return segment.resource
    			}
    		}
    	}
    	
    }
    
    def DataInfo createDataInfo(CmnData data) {
        val List composedResourcePath = newArrayList
        val dataInfo = composeDataInfo(composedResourcePath, data)
        dataInfo.resourcePath = composedResourcePath
        
        /* determine required data */
        val requiredData = newLinkedHashSet
        for (pathSegment : dataInfo.resourcePath) {
            if (pathSegment.parameter !== null) {
                val parameter = pathSegment.parameter
                if (parameter.data !== null) {
                    requiredData.add(parameter.data)
                } else if (parameter.fieldSource instanceof CmnData) {
                    requiredData.add(parameter.fieldSource as CmnData)
                }
            }
        }
        dataInfo.requiredData = requiredData
        
        dataInfo.resourcePath.reverse
        if (!dataInfo.resourcePath.empty) {
            val lastResource = dataInfo.resourcePath.lastResourceOfPath
            if (dataInfo.type === null && lastResource.representation instanceof CmnComplexType) {
                if (dataInfo.resourceOperation?.responses !== null) {
                    val firstResponse = dataInfo.resourceOperation.responses.head
                    if (firstResponse?.content !== null) {
                    	val response = firstResponse.content
                    	val responseType = response.type
                    	if (responseType !== null) {
	                        dataInfo.type = responseType as CmnComplexType
	                        dataInfo.multiple = response.maxOccurs !== null && response.maxOccurs == -1
                    	}
                    }
                }
                if (dataInfo.type === null) {
                	dataInfo.type = lastResource.representation as CmnComplexType
                	dataInfo.multiple = lastResource.maxOccurs !== null && lastResource.maxOccurs == -1
                }
            }
            
            val lastSegment = dataInfo.resourcePath?.last
            val lastIsParameter = lastSegment.parameter !== null // TODO refactore to support multiple parameters
            if (lastIsParameter || dataInfo.itemOfCollection) {
                dataInfo.multiple = false // TODO consider ordering in case of multiple parameters
            }
        }
        if (dataInfo.type instanceof CmnComplexType) {
            val List fieldList = newArrayList
            fieldList.addAll(dataInfo.type.allFields(true))
            dataInfo.fields = fieldList
            dataInfo.keyField = dataInfo.type.keyFieldOfType(false)
        } else {
            dataInfo.fields = Collections.emptyList
        }
        if (dataInfo.resourceOperation !== null) {
            val pagination = dataInfo.resourceOperation.isTrue('pagination')
            val paging = dataInfo.resourceOperation.isTrue('paging')
            if (pagination || paging) {
                dataInfo.supportPagination = true;
            } else {
                 dataInfo.supportPagination = false;
            }
        }
        return dataInfo
    }
    
    def DataInfo composeDataInfo(List composedResourcePath, CmnData data) {
        if (data instanceof CmnReferenceData) {
            addResourcesFromResourcePath(composedResourcePath, data.resourcePath)
            var DataInfo dataInfo
            if (data.referencedComponent !== null) {
            	dataInfo = new DataInfo()
            	dataInfo.referencedComponent = data.referencedComponent
            } else {
            	val referencedData = data.referencedData
            	dataInfo = composeDataInfo(composedResourcePath, referencedData)
            }
            dataInfo.data = data
            dataInfo.name = data.name
            dataInfo.itemOfCollection = data.itemOfCollection;
            dataInfo.queryParameterValues = data.queryParameterValues 
            if (data.resourceOperation !== null) {
                dataInfo.resourceOperation = data.resourceOperation
            }
            if (data.eventHandling !== null) {
                dataInfo.initEventHandling = data.eventHandling
            }
            if (data.type instanceof CmnComplexType) {
                dataInfo.type = data.type as CmnComplexType
                if (data.cardinality !== null) {
                    dataInfo.multiple = data.cardinality.maxOccurs !== null && data.cardinality.maxOccurs == -1
                } else {
                    dataInfo.multiple = false
                }
            }
            return dataInfo
        } else if (data instanceof CmnResourceData) {
            val dataInfo = new DataInfo()
            dataInfo.data = data
            dataInfo.name = data.name
           
            if (data.type !== null) {
                dataInfo.type = data.type as CmnComplexType
                if (data.cardinality !== null) {
                    dataInfo.multiple = data.cardinality.maxOccurs !== null && data.cardinality.maxOccurs == -1
                } else {
                    dataInfo.multiple = false
                }
            }

            val locationProperty = data.getProperty('location')
            if (locationProperty !== null) {
                if (locationProperty.value instanceof CmnPropertyDefinition) {
                    dataInfo.locationProperty = locationProperty.value as CmnPropertyDefinition
                } else {
                    System.out.println("location is not instance of " + CmnPropertyDefinition.canonicalName)
                }
            }
            addResourcesFromResourcePath(composedResourcePath, data.resourcePath)
            dataInfo.itemOfCollection = data.itemOfCollection;
            dataInfo.queryParameterValues = data.queryParameterValues 
            if (data.resourceOperation !== null) {
                dataInfo.resourceOperation = data.resourceOperation
            }
            if (data.eventHandling !== null) {
                dataInfo.initEventHandling = data.eventHandling
            }
            return dataInfo;
        }

    }
    
    def addResourcesFromResourcePath(List composedResourcePath, List resourcePath) {
        if (resourcePath !== null) {
            for (var i = resourcePath.length - 1; i >= 0; i--) {
            	val resourcePathSegment = resourcePath.get(i)
	            composedResourcePath.add(resourcePathSegment)
            }
        }
    }
	
}