| /** |
| * 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 |
| } |
| |
| } |