blob: 8eded8f207c40056e0c0fc488e780d4890f5bf1c [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.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);
}
}