| /** |
| * 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.common.xtext.jvmmodel |
| |
| import com.google.inject.Inject |
| import java.util.List |
| import java.util.ListIterator |
| import javax.validation.Valid |
| import javax.validation.constraints.AssertFalse |
| import javax.validation.constraints.AssertTrue |
| import javax.validation.constraints.DecimalMax |
| import javax.validation.constraints.DecimalMin |
| import javax.validation.constraints.Digits |
| import javax.validation.constraints.Future |
| import javax.validation.constraints.Max |
| import javax.validation.constraints.Min |
| import javax.validation.constraints.NotNull |
| import javax.validation.constraints.Null |
| import javax.validation.constraints.Past |
| import javax.validation.constraints.Pattern |
| import javax.validation.constraints.Size |
| import org.eclipse.emf.ecore.EObject |
| import org.eclipse.osbp.dsl.common.xtext.extensions.AnnotationExtension |
| import org.eclipse.osbp.dsl.semantic.common.types.LAnnotationTarget |
| import org.eclipse.osbp.dsl.semantic.common.types.LAttribute |
| import org.eclipse.osbp.dsl.semantic.common.types.LDataType |
| import org.eclipse.osbp.dsl.semantic.common.types.LDatatypeConstraint |
| import org.eclipse.osbp.dsl.semantic.common.types.LDtCAssertFalse |
| import org.eclipse.osbp.dsl.semantic.common.types.LDtCAssertTrue |
| import org.eclipse.osbp.dsl.semantic.common.types.LDtCDecimalMax |
| import org.eclipse.osbp.dsl.semantic.common.types.LDtCDecimalMin |
| import org.eclipse.osbp.dsl.semantic.common.types.LDtCDigits |
| import org.eclipse.osbp.dsl.semantic.common.types.LDtCFuture |
| import org.eclipse.osbp.dsl.semantic.common.types.LDtCNotNull |
| import org.eclipse.osbp.dsl.semantic.common.types.LDtCNull |
| import org.eclipse.osbp.dsl.semantic.common.types.LDtCNumericMax |
| import org.eclipse.osbp.dsl.semantic.common.types.LDtCNumericMin |
| import org.eclipse.osbp.dsl.semantic.common.types.LDtCPast |
| import org.eclipse.osbp.dsl.semantic.common.types.LDtCRegEx |
| import org.eclipse.osbp.dsl.semantic.common.types.LDtCSize |
| import org.eclipse.osbp.dsl.semantic.common.types.LKeyAndValue |
| import org.eclipse.osbp.dsl.semantic.common.types.LOperation |
| import org.eclipse.osbp.dsl.semantic.common.types.LReference |
| import org.eclipse.osbp.dsl.semantic.common.types.LScalarType |
| import org.eclipse.osbp.dsl.semantic.common.types.LType |
| import org.eclipse.osbp.runtime.common.annotations.Dirty |
| import org.eclipse.osbp.runtime.common.annotations.Properties |
| import org.eclipse.osbp.runtime.common.annotations.Property |
| import org.eclipse.osbp.runtime.common.validation.ErrorSeverity |
| import org.eclipse.xtext.common.types.JvmAnnotationReference |
| import org.eclipse.xtext.common.types.JvmField |
| import org.eclipse.xtext.common.types.JvmGenericType |
| import org.eclipse.xtext.common.types.JvmMember |
| 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.runtime.common.validation.InfoSeverity |
| import org.eclipse.osbp.runtime.common.validation.WarningSeverity |
| |
| /** |
| * This class is responsible to generate the Annotations defined in the entity model |
| */ |
| class AnnotationCompiler { |
| |
| @Inject extension JvmTypesBuilder |
| @Inject extension AnnotationExtension |
| @Inject extension TypeReferences |
| |
| def processAnnotation(LAnnotationTarget annotationTarget, JvmMember jvmMember) { |
| internalProcessAnnotation(annotationTarget, jvmMember) |
| } |
| |
| def protected dispatch void internalProcessAnnotation(LType bean, JvmGenericType jvmType) { |
| bean.resolvedAnnotations.filter([!exclude]).map([annotation]).translateAnnotationsTo(jvmType); |
| } |
| |
| def protected dispatch void internalProcessAnnotation(LReference ref, JvmField jvmType) { |
| ref.resolvedAnnotations.filter([!exclude]).map([annotation]).translateAnnotationsTo(jvmType); |
| } |
| |
| def protected dispatch void internalProcessAnnotation(LAttribute att, JvmField jvmType) { |
| att.resolvedAnnotations.filter([!exclude]).map([annotation]).translateAnnotationsTo(jvmType); |
| } |
| |
| def void toDatatypeBasedConstraintAnnotations(LAttribute att, JvmField jvmType) { |
| if (att.type instanceof LDataType) { |
| val LDataType dt = att.type as LDataType; |
| for (c : dt.constraints) { |
| c.toConstraintAnnotation(jvmType) |
| } |
| } |
| } |
| |
| def protected List<LKeyAndValue> mergeKeyAndValues(LAttribute att) { |
| val keyAndValues = newArrayList() |
| |
| // add all datatype properties to the list |
| att.addDatatypeKeyAndValues(keyAndValues) |
| |
| // add the attribute properties |
| keyAndValues.addAll(att.properties) |
| |
| // remove duplicate keys |
| keyAndValues.removeDuplicateKeys |
| |
| return keyAndValues |
| } |
| |
| def protected List<LKeyAndValue> mergeKeyAndValues(LReference ref) { |
| val keyAndValues = newArrayList() |
| |
| // add the attribute properties |
| keyAndValues.addAll(ref.properties) |
| |
| // remove duplicate keys |
| keyAndValues.removeDuplicateKeys |
| |
| return keyAndValues |
| } |
| |
| def protected void removeDuplicateKeys(List<LKeyAndValue> result) { |
| |
| if (result.size == 0) { |
| return |
| } |
| |
| val keys = newArrayList() |
| |
| val ListIterator<LKeyAndValue> iter = result.listIterator(result.size - 1) |
| while (iter.hasPrevious) { |
| val LKeyAndValue temp = iter.previous |
| if (keys.contains(temp.key)) { |
| iter.remove |
| } else { |
| keys += temp.key |
| } |
| } |
| } |
| |
| def protected addDatatypeKeyAndValues(LAttribute att, List<LKeyAndValue> result) { |
| if (att.type instanceof LDataType) { |
| val LDataType dt = att.type as LDataType |
| result.addAll(dt.properties) |
| } |
| } |
| |
| def protected void toPropertiesAnnotation(EObject context, List<LKeyAndValue> keyValues, JvmField field) { |
| if (keyValues == null || keyValues.size == 0) { |
| return |
| } |
| val innerAnnotations = newArrayList() |
| keyValues.forEach [ |
| val innerAnno = context.toAnnotation(typeof(Property)) |
| innerAnno.addAnnAttr(it, "key", key) |
| innerAnno.addAnnAttr(it, "value", value) |
| innerAnnotations += innerAnno |
| ] |
| |
| // now create the outer annotation and add the array of inner annotations |
| val mainAnno = context.toAnnotation(typeof(Properties)) |
| mainAnno.addAnnAttr(context, "properties", |
| innerAnnotations.toArray(<JvmAnnotationReference>newArrayOfSize(innerAnnotations.length))) |
| field.annotations += mainAnno |
| |
| } |
| |
| /** |
| * Annotation to tell validator, that nested properties should be validated too |
| */ |
| def protected void toValidAnnotation(EObject context, JvmField field) { |
| val anno = context.toAnnotation(typeof(Valid)) |
| field.annotations += anno |
| } |
| |
| /** |
| * Annotation for the dirty state |
| */ |
| def void toDirtyAnnotation(EObject context, JvmField field) { |
| val anno = context.toAnnotation(typeof(Dirty)) |
| field.annotations += anno |
| } |
| |
| def dispatch boolean isValidAllowed(Void voidx) { |
| return false |
| } |
| |
| def dispatch boolean isValidAllowed(LDataType type) { |
| if (type.asPrimitive) { |
| return false |
| } |
| |
| if (type.jvmTypeReference?.qualifiedName.isPrimitiveJvmType) { |
| return false |
| } |
| |
| return true |
| } |
| |
| def dispatch boolean isValidAllowed(LScalarType type) { |
| return false; |
| } |
| |
| def boolean isPrimitiveJvmType(String value) { |
| if (value == null) { |
| return false |
| } |
| |
| switch (value) { |
| case (typeof(Integer).name ): |
| return true |
| case Integer.TYPE.name: |
| return true |
| case typeof(Boolean).name: |
| return true |
| case Boolean.TYPE.name: |
| return true |
| case typeof(Short).name: |
| return true |
| case Short.TYPE.name: |
| return true |
| case typeof(Long).name: |
| return true |
| case Long.TYPE.name: |
| return true |
| case typeof(Double).name: |
| return true |
| case Double.TYPE.name: |
| return true |
| case typeof(Float).name: |
| return true |
| case Float.TYPE.name: |
| return true |
| case typeof(Character).name: |
| return true |
| case Character.TYPE.name: |
| return true |
| case typeof(Byte).name: |
| return true |
| case Byte.TYPE.name: |
| return true |
| case typeof(Boolean).name: |
| return true |
| case Boolean.TYPE.name: |
| return true |
| case typeof(String).name: |
| return true |
| } |
| return false |
| } |
| |
| def protected dispatch void toConstraintAnnotation(LDtCAssertFalse constraint, JvmField field) { |
| val anno = constraint.toAnnotation(typeof(AssertFalse)) |
| handlePayloadAndMessages(constraint, anno) |
| field.annotations += anno |
| } |
| |
| protected def handlePayloadAndMessages(LDatatypeConstraint constraint, JvmAnnotationReference anno) { |
| |
| // TODO OSBP right now vaadin does not support INFO and WARNING. |
| switch (constraint.severity) { |
| case INFO: |
| anno.addAnnAttr(constraint, "payload", typeof(InfoSeverity).getTypeForName(constraint)) |
| case WARNING: |
| anno.addAnnAttr(constraint, "payload", typeof(WarningSeverity).getTypeForName(constraint)) |
| case ERROR: |
| anno.addAnnAttr(constraint, "payload", typeof(ErrorSeverity).getTypeForName(constraint)) |
| } |
| // anno.addAnnAttr(constraint, "payload", typeof(ErrorSeverity).getTypeForName(constraint)) |
| |
| val b = new StringBuilder |
| if (!constraint.msgI18nKey.nullOrEmpty) { |
| b.append(constraint.msgI18nKey) |
| } |
| |
| if (!constraint.msgCode.nullOrEmpty) { |
| b.append(":") |
| b.append(constraint.msgCode) |
| } |
| |
| if (!b.toString.nullOrEmpty) { |
| anno.addAnnAttr(constraint, "message", "{" + b.toString + "}") |
| } |
| } |
| |
| def protected dispatch void toConstraintAnnotation(LDtCAssertTrue constraint, JvmField field) { |
| val anno = constraint.toAnnotation(typeof(AssertTrue)) |
| handlePayloadAndMessages(constraint, anno) |
| field.annotations += anno |
| } |
| |
| def protected dispatch void toConstraintAnnotation(LDtCNotNull constraint, JvmField field) { |
| val anno = constraint.toAnnotation(typeof(NotNull)) |
| handlePayloadAndMessages(constraint, anno) |
| field.annotations += anno |
| } |
| |
| def protected dispatch void toConstraintAnnotation(LDtCNull constraint, JvmField field) { |
| val anno = constraint.toAnnotation(typeof(Null)) |
| handlePayloadAndMessages(constraint, anno) |
| field.annotations += anno |
| } |
| |
| def protected dispatch void toConstraintAnnotation(LDtCFuture constraint, JvmField field) { |
| val anno = constraint.toAnnotation(typeof(Future)) |
| handlePayloadAndMessages(constraint, anno) |
| field.annotations += anno |
| } |
| |
| def protected dispatch void toConstraintAnnotation(LDtCPast constraint, JvmField field) { |
| val anno = constraint.toAnnotation(typeof(Past)) |
| handlePayloadAndMessages(constraint, anno) |
| field.annotations += anno |
| } |
| |
| def protected dispatch void toConstraintAnnotation(LDtCSize constraint, JvmField field) { |
| val anno = constraint.toAnnotation(typeof(Size)) |
| if (constraint.min >= 0) { |
| anno.addAnnAttr(constraint, "min", constraint.min) |
| } |
| if (constraint.max > 0) { |
| anno.addAnnAttr(constraint, "max", constraint.max) |
| } |
| |
| handlePayloadAndMessages(constraint, anno) |
| |
| field.annotations += anno |
| } |
| |
| def protected dispatch void toConstraintAnnotation(LDtCDigits constraint, JvmField field) { |
| val anno = constraint.toAnnotation(typeof(Digits)) |
| if (constraint.intDigits >= 0) { |
| anno.addAnnAttr(constraint, "integer", constraint.intDigits) |
| } |
| if (constraint.fractionDigits > 0) { |
| anno.addAnnAttr(constraint, "fraction", constraint.fractionDigits) |
| } |
| |
| handlePayloadAndMessages(constraint, anno) |
| |
| field.annotations += anno |
| } |
| |
| def protected dispatch void toConstraintAnnotation(LDtCDecimalMax constraint, JvmField field) { |
| val anno = constraint.toAnnotation(typeof(DecimalMax)) |
| anno.addAnnAttr(constraint, "value", constraint.max.toString) |
| handlePayloadAndMessages(constraint, anno) |
| field.annotations += anno |
| } |
| |
| def protected dispatch void toConstraintAnnotation(LDtCDecimalMin constraint, JvmField field) { |
| val anno = constraint.toAnnotation(typeof(DecimalMin)) |
| anno.addAnnAttr(constraint, "value", constraint.min.toString) |
| handlePayloadAndMessages(constraint, anno) |
| field.annotations += anno |
| } |
| |
| def protected dispatch void toConstraintAnnotation(LDtCNumericMax constraint, JvmField field) { |
| val anno = constraint.toAnnotation(typeof(Max)) |
| anno.addAnnAttr(constraint, "value", constraint.max) |
| handlePayloadAndMessages(constraint, anno) |
| field.annotations += anno |
| } |
| |
| def protected dispatch void toConstraintAnnotation(LDtCNumericMin constraint, JvmField field) { |
| val anno = constraint.toAnnotation(typeof(Min)) |
| anno.addAnnAttr(constraint, "value", constraint.min) |
| handlePayloadAndMessages(constraint, anno) |
| field.annotations += anno |
| } |
| |
| def protected dispatch void toConstraintAnnotation(LDtCRegEx constraint, JvmField field) { |
| if (constraint.pattern != null && !constraint.pattern.isEmpty) { |
| val anno = constraint.toAnnotation(typeof(Pattern)) |
| anno.addAnnAttr(constraint, "regexp", constraint.pattern) |
| handlePayloadAndMessages(constraint, anno) |
| field.annotations += anno |
| } |
| } |
| |
| // def protected dispatch void toConstraintAnnotation(LDtCPast constraint, JvmField field) { |
| // field.annotations += constraint.toAnnotation(typeof(Past)) |
| // } |
| def protected dispatch void internalProcessAnnotation(LOperation member, JvmOperation jvmOperation) { |
| member.resolvedAnnotations.filter([!exclude]).map([annotation]).translateAnnotationsTo(jvmOperation); |
| } |
| } |