blob: b424b57aadac4f35dd8956d9787ffdff03fac783 [file] [log] [blame]
/**
* Copyright (c) 2011, 2015 - Lunifera GmbH (Gross Enzersdorf, Austria), Loetz GmbH&Co.KG (69115 Heidelberg, Germany)
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Florian Pirchner - Initial implementation
*/
package org.eclipse.osbp.dsl.entity.xtext.jvmmodel
import com.google.inject.Inject
import java.util.List
import javax.persistence.AssociationOverride
import javax.persistence.AssociationOverrides
import javax.persistence.AttributeOverride
import javax.persistence.AttributeOverrides
import javax.persistence.Basic
import javax.persistence.Cacheable
import javax.persistence.CascadeType
import javax.persistence.Column
import javax.persistence.DiscriminatorColumn
import javax.persistence.DiscriminatorType
import javax.persistence.DiscriminatorValue
import javax.persistence.ElementCollection
import javax.persistence.Embeddable
import javax.persistence.Embedded
import javax.persistence.EmbeddedId
import javax.persistence.Entity
import javax.persistence.EntityListeners
import javax.persistence.FetchType
import javax.persistence.GeneratedValue
import javax.persistence.GenerationType
import javax.persistence.Id
import javax.persistence.Index
import javax.persistence.Inheritance
import javax.persistence.InheritanceType
import javax.persistence.JoinColumn
import javax.persistence.JoinColumns
import javax.persistence.Lob
import javax.persistence.ManyToOne
import javax.persistence.MappedSuperclass
import javax.persistence.OneToMany
import javax.persistence.OneToOne
import javax.persistence.Table
import javax.persistence.Temporal
import javax.persistence.TemporalType
import javax.persistence.Transient
import javax.persistence.Version
import org.eclipse.emf.ecore.resource.Resource
import org.eclipse.osbp.dsl.entity.xtext.extensions.AnnotationExtension
import org.eclipse.osbp.dsl.entity.xtext.extensions.ModelExtensions
import org.eclipse.osbp.dsl.entity.xtext.extensions.NamingExtensions
import org.eclipse.osbp.dsl.entity.xtext.util.PersistenceNamingUtils
import org.eclipse.osbp.dsl.semantic.common.types.LAttributeMatchingConstraint
import org.eclipse.osbp.dsl.semantic.common.types.LDataType
import org.eclipse.osbp.dsl.semantic.common.types.LReference
import org.eclipse.osbp.dsl.semantic.common.types.LResultFilters
import org.eclipse.osbp.dsl.semantic.entity.LBean
import org.eclipse.osbp.dsl.semantic.entity.LBeanAttribute
import org.eclipse.osbp.dsl.semantic.entity.LBeanFeature
import org.eclipse.osbp.dsl.semantic.entity.LBeanReference
import org.eclipse.osbp.dsl.semantic.entity.LDiscriminatorType
import org.eclipse.osbp.dsl.semantic.entity.LEntity
import org.eclipse.osbp.dsl.semantic.entity.LEntityAttribute
import org.eclipse.osbp.dsl.semantic.entity.LEntityInheritanceStrategy
import org.eclipse.osbp.dsl.semantic.entity.LEntityReference
import org.eclipse.osbp.dsl.semantic.entity.LOperation
import org.eclipse.osbp.dsl.semantic.entity.LTablePerClassStrategy
import org.eclipse.osbp.dsl.semantic.entity.LTablePerSubclassStrategy
import org.eclipse.osbp.jpa.services.history.HistorizedQueryRedirector
import org.eclipse.osbp.jpa.services.listener.EntityInfoListener
import org.eclipse.osbp.runtime.common.annotations.AsGrid
import org.eclipse.osbp.runtime.common.annotations.AsKanbanOrdering
import org.eclipse.osbp.runtime.common.annotations.AsKanbanState
import org.eclipse.osbp.runtime.common.annotations.AsTable
import org.eclipse.osbp.runtime.common.annotations.BeanOnTab
import org.eclipse.osbp.runtime.common.annotations.Dispose
import org.eclipse.osbp.runtime.common.annotations.DomainDescription
import org.eclipse.osbp.runtime.common.annotations.DomainKey
import org.eclipse.osbp.runtime.common.annotations.Filter
import org.eclipse.osbp.runtime.common.annotations.Hidden
import org.eclipse.osbp.runtime.common.annotations.HistorizedObject
import org.eclipse.osbp.runtime.common.annotations.OnKanbanCard
import org.eclipse.osbp.runtime.common.annotations.Range
import org.eclipse.osbp.runtime.common.annotations.ReadOnly
import org.eclipse.osbp.runtime.common.annotations.SideKick
import org.eclipse.osbp.runtime.common.annotations.TargetEnumConstraint
import org.eclipse.osbp.runtime.common.annotations.TargetEnumConstraints
import org.eclipse.osbp.runtime.common.annotations.TimedependentObject
import org.eclipse.osbp.runtime.common.annotations.UIGroup
import org.eclipse.osbp.runtime.common.annotations.UniqueEntry
import org.eclipse.persistence.annotations.QueryRedirectors
import org.eclipse.xtext.common.types.JvmAnnotationReference
import org.eclipse.xtext.common.types.JvmAnnotationTarget
import org.eclipse.xtext.common.types.JvmField
import org.eclipse.xtext.common.types.JvmGenericType
import org.eclipse.xtext.common.types.JvmOperation
import org.eclipse.xtext.common.types.util.TypeReferences
import org.eclipse.xtext.xbase.jvmmodel.JvmTypesBuilder
import org.eclipse.osbp.dsl.semantic.common.types.LEnum
import org.eclipse.persistence.annotations.ObjectTypeConverter
import org.eclipse.persistence.annotations.ConversionValue
import org.eclipse.persistence.annotations.Convert
import java.util.Arrays
import org.eclipse.xtext.common.types.JvmDeclaredType
import org.eclipse.xtext.common.types.JvmEnumerationType
import org.eclipse.osbp.runtime.common.annotations.ExtraStyle
/**
* This class is responsible to generate the Annotations defined in the entity model
*/
class AnnotationCompiler extends org.eclipse.osbp.dsl.common.xtext.jvmmodel.AnnotationCompiler {
@Inject extension ModelExtensions
@Inject extension JvmTypesBuilder
@Inject extension AnnotationExtension
@Inject extension NamingExtensions
@Inject TypeReferences references
def protected dispatch void internalProcessAnnotation(LBean bean, JvmGenericType jvmType) {
jvmType.addAnnotations(bean.resolvedAnnotations.filter([!exclude]).map([annotation]))
bean.addAnno(jvmType, bean.toAnnotation(typeof(Embeddable)))
}
def protected dispatch void internalProcessAnnotation(LOperation member, JvmField jvmOperation) {
jvmOperation.addAnnotations(member.resolvedAnnotations.filter([!exclude]).map([annotation]))
}
def protected dispatch void internalProcessAnnotation(LEntity entity, JvmGenericType jvmType) {
jvmType.addAnnotations(entity.resolvedAnnotations.filter([!exclude]).map([annotation]))
if (entity.historized) {
addAnno(entity, jvmType, entity.toAnnotation(typeof(HistorizedObject)))
} else if (entity.timedependent) {
addAnno(entity, jvmType, entity.toAnnotation(typeof(TimedependentObject)))
}
if (entity.historized || entity.timedependent) {
// add the queryRedirector annotation
// @QueryRedirectors(insert = HistorizedQueryRedirector.class, update = HistorizedQueryRedirector.class)
val redirectAnno = entity.toAnnotation(typeof(QueryRedirectors))
val insertType = references.getTypeForName(typeof(HistorizedQueryRedirector), entity)
redirectAnno.addAnnAttr(entity, "insert", insertType)
val updateType = references.getTypeForName(typeof(HistorizedQueryRedirector), entity)
redirectAnno.addAnnAttr(entity, "update", updateType)
addAnno(entity, jvmType, redirectAnno)
}
if (entity.mappedSuperclass) {
addAnno(entity, jvmType, entity.toAnnotation(typeof(MappedSuperclass)))
val listenerAnn = entity.toAnnotation(typeof(EntityListeners))
val listenerType = references.getTypeForName(typeof(EntityInfoListener), entity)
listenerAnn.addAnnAttr(entity, "value", listenerType)
addAnno(entity, jvmType, listenerAnn)
} else {
// @Entity
addAnno(entity, jvmType, entity.toAnnotation(typeof(Entity)))
// if (entity.historized) {
// val ann = entity.toAnnotation(typeof(Customizer))
// ann.addAnnAttr(entity, "value", references.getTypeForName(typeof(HistoryCustomizer), entity))
// }
if (!entity.isStrategyFromSuperPresent || entity.isStrategyPerSubclass) {
// @Table
val tableAnn = entity.toAnnotation(typeof(Table))
addAnno(entity, jvmType, tableAnn)
val schemaName = entity.toSchemaName
if (!schemaName.nullOrEmpty) {
tableAnn.addAnnAttr(entity, "schema", schemaName)
}
tableAnn.addAnnAttr(entity, "name", entity.toTableName)
// @Index
//
val List<JvmAnnotationReference> collectedIndizes = newArrayList();
for (index : entity.indexes) {
val indexAnn = entity.toAnnotation(typeof(Index))
addAnno(entity, jvmType, indexAnn)
indexAnn.addAnnAttr(entity, "name", PersistenceNamingUtils::camelCaseToUpperCase(entity.name+index.name))
if (index.unique) {
indexAnn.addAnnAttr(entity, "unique", true)
}
var StringBuilder propList = new StringBuilder;
for (name : index.features.map[it.name]) {
if (propList != null) {
if (propList.length > 0) {
propList.append(", ")
}
propList.append(PersistenceNamingUtils::camelCaseToUpperCase(name));
}
}
indexAnn.addAnnAttr(entity, "columnList", propList.toString)
collectedIndizes.add(indexAnn);
}
if (collectedIndizes.size > 0) {
val JvmAnnotationReference[] result = collectedIndizes.toArray(
newArrayOfSize(collectedIndizes.size));
tableAnn.addAnnAttr(entity, "indexes", result)
}
}
// @Inheritance
val LEntityInheritanceStrategy strategy = entity.toInheritanceStrategy
strategy.processInheritance(entity, jvmType)
// @Cachable
if (entity.cacheable) {
addAnno(entity, jvmType, entity.toAnnotation(typeof(Cacheable)))
}
}
}
def protected dispatch void processInheritance(LTablePerClassStrategy strategy, LEntity entity,
JvmGenericType jvmType) {
// Process inheritance.
// If the Entity has subclasses, setup @Inheritance
val superType = entity.superType
if (!entity.subTypes.empty && (superType == null || superType.checkIsMappedSuperclass)) {
val annRef = entity.toAnnotation(typeof(Inheritance))
annRef.addAnnAttr(entity, "strategy", InheritanceType::SINGLE_TABLE)
addAnno(entity, jvmType, annRef)
val discrColumn = entity.toAnnotation(typeof(DiscriminatorColumn))
discrColumn.addAnnAttr(entity, "name", strategy.discriminatorColumn)
discrColumn.addAnnAttr(entity, "discriminatorType", strategy.discriminatorType.toDiscriminatorType)
addAnno(entity, jvmType, discrColumn)
}
// add the discriminator value only once
if (!entity.subTypes.empty || !superType.checkIsMappedSuperclass) {
val discrValue = entity.toAnnotation(typeof(DiscriminatorValue))
discrValue.addAnnAttr(entity, "value", strategy.discriminatorValue)
addAnno(entity, jvmType, discrValue)
}
}
def protected dispatch void processInheritance(LTablePerSubclassStrategy strategy, LEntity entity,
JvmGenericType jvmType) {
// Process inheritance.
// If the Entity has subclasses, setup @Inheritance
val superType = entity.superType
if (!entity.subTypes.empty && (superType == null || superType.checkIsMappedSuperclass)) {
val annRef = entity.toAnnotation(typeof(Inheritance))
annRef.addAnnAttr(entity, "strategy", InheritanceType::JOINED)
addAnno(entity, jvmType, annRef)
val discrColumn = entity.toAnnotation(typeof(DiscriminatorColumn))
discrColumn.addAnnAttr(entity, "name", strategy.discriminatorColumn)
discrColumn.addAnnAttr(entity, "discriminatorType", strategy.discriminatorType.toDiscriminatorType)
addAnno(entity, jvmType, discrColumn)
}
// add the discriminator value only once
if (!entity.subTypes.empty || !superType.checkIsMappedSuperclass) {
val discrValue = entity.toAnnotation(typeof(DiscriminatorValue))
discrValue.addAnnAttr(entity, "value", strategy.discriminatorValue)
addAnno(entity, jvmType, discrValue)
}
}
def DiscriminatorType toDiscriminatorType(LDiscriminatorType type) {
switch (type) {
case LDiscriminatorType::STRING:
return DiscriminatorType::STRING
case LDiscriminatorType::CHAR:
return DiscriminatorType::CHAR
case LDiscriminatorType::INTEGER:
return DiscriminatorType::INTEGER
default: {
}
}
}
def protected dispatch void internalProcessAnnotation(LEntityReference prop, JvmField jvmField) {
jvmField.addAnnotations(prop.resolvedAnnotations.filter([!exclude]).map([annotation]))
if (prop.toMany) {
// *toMany
if (prop.opposite.toMany) {
// @ManyToMany
addManyToManyAnno(prop, jvmField)
} else {
// @OneToMany
addOneToManyAnno(prop, jvmField)
}
} else {
// *toOne
val opposite = prop.resolvedOpposite
// When we have no opposite, then the master-side has no collection
// and we assume a many-to-one relation.
// A one-to-one relation needs an "opposite" on both sides.
if (opposite != null && !opposite.toMany) {
// @OneToOne
addOneToOneAnno(prop, jvmField)
} else {
// @ManyToOne
addManyToOneAnno(prop, jvmField)
}
}
if (prop.resultFilters != null) {
prop.resultFilters.addConstraintsAnno(jvmField)
}
// create the properties annotations
prop.toPropertiesAnnotation(mergeKeyAndValues(prop), jvmField)
toConstraintAnnotations(prop, jvmField)
}
def protected dispatch void internalProcessAnnotation(LEntityAttribute prop, JvmField jvmField) {
jvmField.addAnnotations(prop.resolvedAnnotations.filter([!exclude]).map([annotation]))
if (prop.transient) {
addAnno(prop, jvmField, prop.toAnnotation(typeof(Transient)))
if (prop.attributeHidden) {
jvmField.annotations += prop.toAnnotation(typeof(Hidden))
} else if (prop.attributeReadOnly) {
jvmField.annotations += prop.toAnnotation(typeof(ReadOnly))
}
if (prop.extraStyle !== null) {
val styleAnnotation = prop.toAnnotation(typeof(ExtraStyle))
styleAnnotation.addAnnAttr(prop, "name", prop.extraStyle)
jvmField.annotations += styleAnnotation
}
} else {
if (prop.id) {
if (prop.entity.historizedOrTimedependentWithParent) {
jvmField.annotations += prop.toAnnotation(typeof(EmbeddedId))
} else {
jvmField.annotations += prop.toAnnotation(typeof(Id))
var genValueAnn = prop.toAnnotation(typeof(GeneratedValue))
genValueAnn.addAnnAttr(prop, "strategy", GenerationType.IDENTITY)
jvmField.annotations += genValueAnn
}
} else if (prop.uuid) {
jvmField.annotations += prop.toAnnotation(typeof(Id))
}
if (prop.version) {
jvmField.annotations += prop.toAnnotation(typeof(Version))
}
if (prop.attributeHidden) {
jvmField.annotations += prop.toAnnotation(typeof(Hidden))
} else if (prop.attributeReadOnly) {
jvmField.annotations += prop.toAnnotation(typeof(ReadOnly))
}
if (prop.extraStyle !== null) {
val styleAnnotation = prop.toAnnotation(typeof(ExtraStyle))
styleAnnotation.addAnnAttr(prop, "name", prop.extraStyle)
jvmField.annotations += styleAnnotation
}
if (prop.toMany) {
val ann = prop.toAnnotation(typeof(ElementCollection))
addAnno(prop, jvmField, ann)
} else {
//
// if the propery is a bean, then add AttributeOverride Annotations
//
if (prop.type instanceof LBean) {
addAnno(prop, jvmField, prop.toAnnotation(typeof(Embedded)))
if ((prop.type as LBean).beanOnTab) {
addAnno(prop, jvmField, prop.toAnnotation(typeof(BeanOnTab)))
}
jvmField.toAttributesOverride(prop)
}
if (prop.domainKey) {
addAnno(prop, jvmField, prop.toAnnotation(typeof(DomainKey)))
}
if (prop.domainDescription) {
addAnno(prop, jvmField, prop.toAnnotation(typeof(DomainDescription)))
}
}
if (!jvmField.isColumnAnnoCreated(prop)) {
val ann = prop.toAnnotation(typeof(Column))
ann.addAnnAttr(prop, "name", prop.toColumnName)
if (prop.bounds.required) {
ann.addAnnAttr(prop, "nullable", false)
}
addAnno(prop, jvmField, ann)
}
if(prop.type instanceof LEnum) {
// @ObjectTypeConverter(name = "genderConverter", objectType = Gender.class, dataType = String.class, conversionValues = {
// @ConversionValue(objectValue = "Male", dataValue = "M"),
// @ConversionValue(objectValue = "Female", dataValue = "F"),
// @ConversionValue(objectValue = "Indifferent", dataValue = "I")
// })
// @Convert("genderConverter")
val LEnum enumType = prop.type as LEnum
val valueAnnotations = <JvmAnnotationReference>newArrayList()
if(enumType.needsConverter(prop, valueAnnotations)) {
val converter = prop.toAnnotation(typeof(ObjectTypeConverter))
converter.addAnnAttr(prop, "name", prop.name+"Converter")
converter.addAnnAttr(prop, "objectType", references.getTypeForName(prop.type.toQualifiedName, prop))
converter.addAnnAttr(prop, "dataType", references.getTypeForName(typeof(String), prop))
converter.addAnnAttr(prop, "conversionValues", valueAnnotations.toArray(<JvmAnnotationReference>newArrayOfSize(valueAnnotations.length)))
addAnno(prop, jvmField, converter)
val converterRef = prop.toAnnotation(typeof(Convert), prop.name+"Converter")
addAnno(prop, jvmField, converterRef)
}
}
if (prop.type instanceof LDataType) {
val LDataType datatype = prop.type as LDataType
if (datatype.date) {
val temp = prop.toAnnotation(typeof(Temporal))
switch (datatype.dateType) {
case DATE:
temp.addAnnAttr(prop, "value", TemporalType::DATE)
case TIME:
temp.addAnnAttr(prop, "value", TemporalType::TIME)
case TIMESTAMP:
temp.addAnnAttr(prop, "value", TemporalType::TIMESTAMP)
default: {
}
}
addAnno(prop, jvmField, temp)
} else if (datatype.asBlob) {
addAnno(prop, jvmField, prop.toAnnotation(typeof(Lob)))
val basic = prop.toAnnotation(typeof(Basic))
basic.addAnnAttr(prop, "fetch", FetchType::LAZY)
addAnno(prop, jvmField, basic)
}
// add the range and filter annotation
if (prop.isFiltering) {
addAnno(prop, jvmField, prop.toAnnotation(typeof(Filter)))
}
if (prop.isRangeFiltering) {
addAnno(prop, jvmField, prop.toAnnotation(typeof(Range)))
}
if (prop.isUniqueEntry) {
addAnno(prop, jvmField, prop.toAnnotation(typeof(UniqueEntry)))
}
}
if (prop.isOnKanbanCard) {
addAnno(prop, jvmField, prop.toAnnotation(typeof(OnKanbanCard)))
}
if (prop.isAsKanbanOrdering) {
addAnno(prop, jvmField, prop.toAnnotation(typeof(AsKanbanOrdering)))
}
if (prop.asKanbanState) {
addAnno(prop, jvmField, prop.toAnnotation(typeof(AsKanbanState)))
}
if (prop.isGrouped) {
val groupAnnotation = prop.toAnnotation(typeof(UIGroup))
groupAnnotation.addAnnAttr(prop, "name", prop.groupName)
addAnno(prop, jvmField, groupAnnotation)
}
}
// create the properties annotations
prop.toPropertiesAnnotation(mergeKeyAndValues(prop), jvmField)
toConstraintAnnotations(prop, jvmField)
}
def boolean needsConverter(LEnum lEnum, LEntityAttribute prop, List<JvmAnnotationReference> valueAnnotations) {
var needsConverter = false
for(literal:lEnum.literals) {
if(literal.stringValue !== null) {
needsConverter = true
val anno = prop.toAnnotation(typeof(ConversionValue))
anno.addAnnAttr(prop, "objectValue", literal.name)
anno.addAnnAttr(prop, "dataValue", literal.stringValue)
valueAnnotations += anno
}
}
return needsConverter
}
// generate Attribute overrides
// @AttributeOverrides({
// @AttributeOverride(name="number", column=@Column(name="ORDER_NUMBER"))
// @AttributeOverride(name="status", column=@Column(name="ORDER_STATUS"))}
// )
def protected void toAttributesOverride(JvmField jvmField, LEntityAttribute prop) {
val List<JvmAnnotationReference> collectedAttributes = newArrayList();
val List<JvmAnnotationReference> collectedAssocations = newArrayList();
for (LBeanFeature f : (prop.type as LBean).allFeatures.filter[!it.toMany]) {
if (f instanceof LBeanAttribute) {
val overrideAttributeAnno = prop.toAnnotation(typeof(AttributeOverride))
overrideAttributeAnno.addAnnAttr(f, "name", f.toName)
{
val colAnno = prop.toAnnotation(typeof(Column))
colAnno.addAnnAttr(f, "name", (prop.toName + "_" + f.toName).toUpperCase)
overrideAttributeAnno.addAnnAttr(f, "column", colAnno)
}
collectedAttributes += overrideAttributeAnno;
} else if (f instanceof LBeanReference) {
val type = f.type
switch (type) {
LEntity: {
val overrideAssociationAnno = prop.toAnnotation(typeof(AssociationOverride))
overrideAssociationAnno.addAnnAttr(f, "name", f.toName)
{
val colAnno = prop.toAnnotation(typeof(JoinColumn))
colAnno.addAnnAttr(f, "name", (prop.toName + "_" + f.toName).toUpperCase)
overrideAssociationAnno.addAnnAttr(f, "joinColumns", colAnno)
}
collectedAssocations += overrideAssociationAnno;
}
LBean: {
type.collectNestedAttributeOverride(collectedAttributes, f.toName,
(prop.toName + "_" + f.toName).toUpperCase)
}
}
}
}
if (!collectedAttributes.empty) {
val overrideAttributesAnno = prop.toAnnotation(typeof(AttributeOverrides))
val JvmAnnotationReference[] result = collectedAttributes.toArray(newArrayOfSize(collectedAttributes.size));
overrideAttributesAnno.addAnnAttr(prop, "value", result)
addAnno(prop, jvmField, overrideAttributesAnno)
}
if (!collectedAssocations.empty) {
val overrideAssociationsAnno = prop.toAnnotation(typeof(AssociationOverrides))
val JvmAnnotationReference[] result = collectedAssocations.toArray(
newArrayOfSize(collectedAssocations.size));
overrideAssociationsAnno.addAnnAttr(prop, "value", result)
addAnno(prop, jvmField, overrideAssociationsAnno)
}
}
/**
* Collects all nested embedded fields to be overridden
*/
def protected void collectNestedAttributeOverride(LBean bean, List<JvmAnnotationReference> collectedReferences,
String propertyPath, String persistencePath) {
if (bean == null) {
return
}
for (LBeanFeature f : bean.allFeatures.filter[!it.toMany]) {
if (f instanceof LBeanAttribute) {
val overrideAttributeAnno = bean.toAnnotation(typeof(AttributeOverride))
overrideAttributeAnno.addAnnAttr(f, "name", propertyPath + "." + f.toName)
{
val colAnno = bean.toAnnotation(typeof(Column))
colAnno.addAnnAttr(f, "name", (persistencePath + "_" + f.toName).toUpperCase)
overrideAttributeAnno.addAnnAttr(f, "column", colAnno)
}
collectedReferences += overrideAttributeAnno;
} else if (f instanceof LBeanReference) {
val oppositeType = f.opposite?.type
val type = f.type
switch (type) {
LEntity: {
// TODO implement
}
LBean: {
if (oppositeType != bean) {
type.collectNestedAttributeOverride(collectedReferences, propertyPath + "." + f.toName,
persistencePath + "_" + f.toName.toUpperCase)
}
}
}
}
}
}
def protected dispatch void internalProcessAnnotation(LBeanAttribute prop, JvmField jvmField) {
jvmField.addAnnotations(prop.resolvedAnnotations.filter([! exclude]).map([annotation]))
if (prop.transient) {
jvmField.annotations += prop.toAnnotation(typeof(Transient))
}
var basicAdded = false;
if (prop.type instanceof LDataType) {
val LDataType datatype = prop.type as LDataType
if (datatype.date) {
addAnno(prop, jvmField, prop.toAnnotation(typeof(Basic)))
basicAdded = true;
val temp = prop.toAnnotation(typeof(Temporal))
switch (datatype.dateType) {
case DATE:
temp.addAnnAttr(prop, "value", TemporalType::DATE)
case TIME:
temp.addAnnAttr(prop, "value", TemporalType::TIME)
case TIMESTAMP:
temp.addAnnAttr(prop, "value", TemporalType::TIMESTAMP)
}
addAnno(prop, jvmField, temp)
} else if (datatype.asBlob) {
addAnno(prop, jvmField, prop.toAnnotation(typeof(Lob)))
val basic = prop.toAnnotation(typeof(Basic))
basic.addAnnAttr(prop, "fetch", FetchType::LAZY)
addAnno(prop, jvmField, basic)
basicAdded = true;
}
// add the range and filter annotation
if (prop.isFiltering) {
prop.toAnnotation(typeof(Filter))
}
if (prop.isRangeFiltering) {
prop.toAnnotation(typeof(Range))
}
}
if (!basicAdded) {
addAnno(prop, jvmField, prop.toAnnotation(typeof(Basic)))
basicAdded = true
}
if (prop.toMany) {
val ann = prop.toAnnotation(typeof(ElementCollection))
addAnno(prop, jvmField, ann)
}
// create the properties annotations
prop.toPropertiesAnnotation(mergeKeyAndValues(prop), jvmField)
toConstraintAnnotations(prop, jvmField)
}
def dispatch void toConstraintAnnotations(LBeanAttribute prop, JvmField jvmField) {
if (prop.type.validAllowed) {
prop.toValidAnnotation(jvmField)
}
for (c : prop.constraints) {
c.toConstraintAnnotation(jvmField)
}
if (prop.constraints.empty) {
super.toDatatypeBasedConstraintAnnotations(prop, jvmField)
}
}
def dispatch void toConstraintAnnotations(LBeanReference prop, JvmField jvmField) {
if (prop.type instanceof LBean && !prop.bounds.toMany) {
prop.toValidAnnotation(jvmField)
}
for (c : prop.constraints) {
c.toConstraintAnnotation(jvmField)
}
}
def dispatch void toConstraintAnnotations(LEntityAttribute prop, JvmField jvmField) {
if (prop.type.validAllowed) {
prop.toValidAnnotation(jvmField)
}
for (c : prop.constraints) {
c.toConstraintAnnotation(jvmField)
}
if (prop.constraints.empty) {
super.toDatatypeBasedConstraintAnnotations(prop, jvmField)
}
}
def dispatch void toConstraintAnnotations(LEntityReference prop, JvmField jvmField) {
if (prop.cascading && !prop.bounds.toMany) {
prop.toValidAnnotation(jvmField)
}
for (c : prop.constraints) {
c.toConstraintAnnotation(jvmField)
}
}
def protected dispatch void internalProcessAnnotation(LBeanReference prop, JvmField jvmField) {
jvmField.addAnnotations(prop.resolvedAnnotations.filter([!exclude]).map([annotation]))
if (prop.type instanceof LEntity) {
// its a reference to an entity and so we need to express the relation
if (prop.toMany) {
// *toMany
if (prop.opposite.toMany) {
// @ManyToMany
addManyToManyAnno(prop, jvmField)
} else {
// @OneToMany
addOneToManyAnno(prop, jvmField)
}
} else {
// *toOne
val opposite = prop.resolvedOpposite
// When we have no opposite, then the master-side has no collection
// and we assume a many-to-one relation.
// A one-to-one relation needs an "opposite" on both sides.
if (opposite != null && !opposite.toMany) {
// @OneToOne
addOneToOneAnno(prop, jvmField)
} else {
// @ManyToOne
addManyToOneAnno(prop, jvmField)
}
}
if (prop.resultFilters !== null) {
prop.resultFilters.addConstraintsAnno(jvmField)
}
} else {
// it is a bean and needs @embedded annotations
jvmField.annotations += prop.toAnnotation(typeof(Basic))
addAnno(prop, jvmField, prop.toAnnotation(typeof(Embedded)))
if (prop.toMany) {
addAnno(prop, jvmField, prop.toAnnotation(typeof(ElementCollection)))
}
}
// create the properties annotations
prop.toPropertiesAnnotation(mergeKeyAndValues(prop), jvmField)
toConstraintAnnotations(prop, jvmField)
}
def private addOneToManyAnno(LReference prop, JvmAnnotationTarget jvmAnnTarget) {
val col = prop.toAnnotation(typeof(JoinColumn))
col.addAnnAttr(prop, "name", prop.toColumnName)
if (prop.bounds.required) {
col.addAnnAttr(prop, "nullable", false)
}
addAnno(prop, jvmAnnTarget, col)
val ann = prop.toAnnotation(typeof(OneToMany))
if (prop.opposite !== null) {
if (prop.opposite.name === null) {
ann.addAnnAttr(prop, "mappedBy", "")
} else {
ann.addAnnAttr(prop, "mappedBy", prop.opposite.name)
}
}
// ATTENTION - never ever touch cascading. Jörg und Flo
//
val cascaded = <CascadeType>newArrayList
if (prop.cascadeRemove) {
cascaded.add(CascadeType::REMOVE)
if (prop.cascadeMergePersist) {
cascaded.add(CascadeType::MERGE)
cascaded.add(CascadeType::PERSIST)
}
if (prop.cascadeRefresh) {
cascaded.add(CascadeType::REFRESH)
}
ann.addAnnAttr(prop, "cascade", cascaded.toArray(<Enum>newArrayOfSize(cascaded.length)))
ann.addAnnAttr(prop, "orphanRemoval", true)
ann.addAnnAttr(prop, "fetch", FetchType::EAGER)
} else if (prop.cascadeMergePersist) {
cascaded.add(CascadeType::MERGE)
cascaded.add(CascadeType::PERSIST)
if (prop.cascadeRefresh) {
cascaded.add(CascadeType::REFRESH)
}
ann.addAnnAttr(prop, "cascade", cascaded.toArray(<Enum>newArrayOfSize(cascaded.length)))
ann.addAnnAttr(prop, "orphanRemoval", false)
ann.addAnnAttr(prop, "fetch", FetchType::LAZY)
} else if (prop.cascadeRefresh) {
cascaded.add(CascadeType::REFRESH)
ann.addAnnAttr(prop, "cascade", cascaded.toArray(<Enum>newArrayOfSize(cascaded.length)))
ann.addAnnAttr(prop, "orphanRemoval", false)
ann.addAnnAttr(prop, "fetch", FetchType::LAZY)
}
addAnno(prop, jvmAnnTarget, ann)
// this leads to unwanted selects during merge
// addAnno(prop, jvmAnnTarget, prop.toAnnotation(typeof(Noncacheable)))
if (prop.isGrouped) {
val groupAnnotation = prop.toAnnotation(typeof(UIGroup))
groupAnnotation.addAnnAttr(prop, "name", prop.groupName)
addAnno(prop, jvmAnnTarget, groupAnnotation)
}
if (prop.asGrid) {
addAnno(prop, jvmAnnTarget, prop.toAnnotation(typeof(AsGrid)))
}
if (prop.asTable) {
addAnno(prop, jvmAnnTarget, prop.toAnnotation(typeof(AsTable)))
}
}
def private addManyToManyAnno(LReference prop, JvmAnnotationTarget jvmAnnTarget) {
// throw new UnsupportedOperationException("ManyToMany not yet supported");
}
def private addManyToOneAnno(LReference prop, JvmAnnotationTarget jvmAnnTarget) {
val manyToOne = prop.toAnnotation(typeof(ManyToOne))
if (prop.bounds.required) {
manyToOne.addAnnAttr(prop, "optional", !prop.bounds.required)
}
manyToOne.addAnnAttr(prop, "fetch", FetchType::LAZY)
val opposite = prop.resolvedOpposite
addAnno(prop, jvmAnnTarget, manyToOne)
var shouldCreateHistorized = false;
val targetType = prop.type
if (targetType instanceof LEntity) {
if (targetType.isHistorizedOrTimedependentWithParent) {
shouldCreateHistorized = true;
}
}
if (!shouldCreateHistorized) {
val joinColumn = prop.toAnnotation(typeof(JoinColumn))
joinColumn.addAnnAttr(prop, "name", prop.toColumnName)
if (prop.bounds.required) {
joinColumn.addAnnAttr(prop, "nullable", false)
}
addAnno(prop, jvmAnnTarget, joinColumn)
} else {
// create an attribution override for the historized key.
// Contains id.id and id.validFrom from target entity.
// eg: @JoinColumns(value=
// {@JoinColumn(name="ADDR_ID", referencedColumnName="ID"),
// @JoinColumn(name="ADDR_VALIDFROM", referencedColumnName="VALIDFROM")
// })
val joinColumns = prop.toAnnotation(typeof(JoinColumns))
addAnno(prop, jvmAnnTarget, joinColumns)
val columns = newArrayList()
// id column
val idJoinColumn = prop.toAnnotation(typeof(JoinColumn))
idJoinColumn.addAnnAttr(prop, "name", prop.toColumnName + "_ID")
idJoinColumn.addAnnAttr(prop, "referencedColumnName", "ID")
if (prop.bounds.required) {
idJoinColumn.addAnnAttr(prop, "nullable", false)
}
columns += idJoinColumn
// validFrom id column
val validFromJoinColumn = prop.toAnnotation(typeof(JoinColumn))
validFromJoinColumn.addAnnAttr(prop, "name", prop.toColumnName + "_VALIDFROM")
validFromJoinColumn.addAnnAttr(prop, "referencedColumnName", "VALIDFROM")
if (prop.bounds.required) {
validFromJoinColumn.addAnnAttr(prop, "nullable", false)
}
columns += validFromJoinColumn
// add columns to @JoinColumns
joinColumns.addAnnAttr(prop, "value",
columns.toArray(<JvmAnnotationReference>newArrayOfSize(columns.length)))
}
if (prop.sideKick) {
addAnno(prop, jvmAnnTarget, prop.toAnnotation(typeof(SideKick)))
}
if (prop.referenceHidden) {
addAnno(prop, jvmAnnTarget, prop.toAnnotation(typeof(Hidden)))
}
if (prop.referenceReadOnly) {
addAnno(prop, jvmAnnTarget, prop.toAnnotation(typeof(ReadOnly)))
}
}
def private addOneToOneAnno(LReference prop, JvmAnnotationTarget jvmAnnTarget) {
val oneToOne = prop.toAnnotation(typeof(OneToOne))
if (prop.bounds.required) {
oneToOne.addAnnAttr(prop, "optional", !prop.bounds.required)
}
val opposite = prop.resolvedOpposite
if (opposite != null && (prop.cascadeMergePersist || prop.cascadeRemove)) {
oneToOne.addAnnAttr(prop, "mappedBy", if(opposite.name != null) opposite.name else "")
}
val cascaded = <CascadeType>newArrayList
if (prop.cascadeRemove) {
cascaded.add(CascadeType::REMOVE)
if (prop.cascadeMergePersist) {
cascaded.add(CascadeType::MERGE)
cascaded.add(CascadeType::PERSIST)
}
oneToOne.addAnnAttr(prop, "cascade", cascaded.toArray(<Enum>newArrayOfSize(cascaded.length)))
oneToOne.addAnnAttr(prop, "orphanRemoval", true)
oneToOne.addAnnAttr(prop, "fetch", FetchType::EAGER)
} else if (prop.cascadeMergePersist) {
cascaded.add(CascadeType::MERGE)
cascaded.add(CascadeType::PERSIST)
oneToOne.addAnnAttr(prop, "cascade", cascaded.toArray(<Enum>newArrayOfSize(cascaded.length)))
oneToOne.addAnnAttr(prop, "orphanRemoval", false)
oneToOne.addAnnAttr(prop, "fetch", FetchType::LAZY)
}
addAnno(prop, jvmAnnTarget, oneToOne)
if (opposite != null && opposite.cascading) {
val joinColumn = prop.toAnnotation(typeof(JoinColumn))
joinColumn.addAnnAttr(prop, "name", prop.toColumnName)
if (prop.bounds.required) {
joinColumn.addAnnAttr(prop, "nullable", false)
}
addAnno(prop, jvmAnnTarget, joinColumn)
}
if (prop.sideKick) {
addAnno(prop, jvmAnnTarget, prop.toAnnotation(typeof(SideKick)))
}
if (prop.referenceHidden) {
addAnno(prop, jvmAnnTarget, prop.toAnnotation(typeof(Hidden)))
}
if (prop.referenceReadOnly) {
addAnno(prop, jvmAnnTarget, prop.toAnnotation(typeof(ReadOnly)))
}
}
def void addConstraintsAnno(LResultFilters constraints, JvmField jvmField) {
// process the LAttributeMatchingConstraint
if (!constraints.resultFilters.filter[it instanceof LAttributeMatchingConstraint].empty) {
// collect all inner annotations
val innerAnnotations = newArrayList()
constraints.resultFilters.filter[it instanceof LAttributeMatchingConstraint].map [
it as LAttributeMatchingConstraint
].forEach [
val innerAnno = constraints.toAnnotation(typeof(TargetEnumConstraint))
innerAnno.addAnnAttr(it, "targetProperty", attribute.name)
innerAnno.addAnnAttr(it, "enumClass", attribute.toTypeReference)
innerAnno.addAnnAttr(it, "enumLiteral", matchingLiteral.name)
innerAnnotations += innerAnno
]
// now create the outer annotation and add the array of inner annotations
val mainAnno = constraints.toAnnotation(typeof(TargetEnumConstraints))
mainAnno.addAnnAttr(constraints, "constraints",
innerAnnotations.toArray(<JvmAnnotationReference>newArrayOfSize(innerAnnotations.length)))
jvmField.annotations += mainAnno
}
}
def dispatch addDisposeFieldAnnotation(LEntity entity, JvmField field) {
val anno = entity.toAnnotation(typeof(Transient))
addAnno(entity, field, anno)
addAnno(entity, field, entity.toAnnotation(typeof(Dispose)))
}
def dispatch addDisposeFieldAnnotation(LBean entity, JvmField field) {
val anno = entity.toAnnotation(typeof(Transient))
addAnno(entity, field, anno)
addAnno(entity, field, entity.toAnnotation(typeof(Dispose)))
}
def dispatch addDisposeFieldAnnotation(LEntity entity, JvmOperation op) {
addAnno(entity, op, entity.toAnnotation(typeof(Dispose)))
}
def dispatch addDisposeFieldAnnotation(LBean entity, JvmOperation op) {
addAnno(entity, op, entity.toAnnotation(typeof(Dispose)))
}
def dispatch boolean isValidAllowed(LBean type) {
return true;
}
/**
* @noreference This method is called by the framework
* @nooverride
*/
def void setContext(Resource resource) {
// _annotationTypesBuilder = annotationRefBuilderFactory.create(resource.getResourceSet());
// _typeReferenceBuilder = typeRefBuilderFactory.create(resource.getResourceSet());
println
}
}