| /** |
| * 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.dto.xtext.jvmmodel |
| |
| import com.google.inject.Inject |
| import java.beans.PropertyChangeEvent |
| import java.beans.PropertyChangeListener |
| import java.io.Serializable |
| import org.eclipse.emf.ecore.EObject |
| import org.eclipse.osbp.dsl.common.datatypes.IDto |
| import org.eclipse.osbp.dsl.dto.lib.IMapper |
| import org.eclipse.osbp.dsl.dto.lib.IMapperAccess |
| import org.eclipse.osbp.dsl.dto.xtext.extensions.AnnotationExtension |
| import org.eclipse.osbp.dsl.dto.xtext.extensions.DtoModelExtensions |
| import org.eclipse.osbp.dsl.dto.xtext.extensions.DtoTypesBuilder |
| import org.eclipse.osbp.dsl.dto.xtext.extensions.MethodNamingExtensions |
| import org.eclipse.osbp.dsl.semantic.common.types.LAttribute |
| import org.eclipse.osbp.dsl.semantic.common.types.LEnum |
| 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.LTypedPackage |
| import org.eclipse.osbp.dsl.semantic.dto.LDto |
| import org.eclipse.osbp.dsl.semantic.dto.LDtoAbstractAttribute |
| import org.eclipse.osbp.dsl.semantic.dto.LDtoAbstractReference |
| import org.eclipse.osbp.dsl.semantic.entity.LEntity |
| import org.eclipse.osbp.dsl.semantic.entity.LEntityAttribute |
| import org.eclipse.osbp.xtext.oxtype.logger.TimeLogger |
| import org.eclipse.osbp.xtext.oxtype.resource.ExtendedModelInferrer |
| import org.eclipse.xtext.common.types.JvmDeclaredType |
| import org.eclipse.xtext.common.types.JvmField |
| import org.eclipse.xtext.common.types.JvmGenericType |
| import org.eclipse.xtext.common.types.JvmParameterizedTypeReference |
| import org.eclipse.xtext.common.types.JvmType |
| import org.eclipse.xtext.common.types.TypesFactory |
| import org.eclipse.xtext.common.types.util.TypeReferences |
| import org.eclipse.xtext.naming.IQualifiedNameProvider |
| import org.eclipse.xtext.xbase.jvmmodel.IJvmDeclaredTypeAcceptor |
| import org.slf4j.Logger |
| import org.slf4j.LoggerFactory |
| import org.eclipse.osbp.dsl.common.datatypes.IDtoHistorized |
| |
| /** |
| * <p>Infers a JVM model from the source model.</p> |
| * |
| * <p>The JVM model should contain all elements that would appear in the Java code |
| * which is generated from the source model. Other models link against the JVM model rather than the source model.</p> |
| */ |
| class DtoGrammarJvmModelInferrer extends ExtendedModelInferrer { |
| |
| protected val Logger log = LoggerFactory::getLogger(getClass()) |
| |
| @Inject AnnotationCompiler annotationCompiler |
| |
| @Inject extension IQualifiedNameProvider |
| @Inject extension DtoTypesBuilder; |
| @Inject extension MethodNamingExtensions |
| @Inject extension DtoModelExtensions; |
| @Inject TypeReferences references |
| @Inject AnnotationExtension annExt |
| |
| def dispatch void inferFullState(JvmType type, EObject element, IJvmDeclaredTypeAcceptor acceptor, |
| boolean isPrelinkingPhase, String selector) { |
| } |
| |
| // used for test cases with old derived state computer |
| def dispatch void infer(LDto dto, IJvmDeclaredTypeAcceptor acceptor, boolean isPrelinkingPhase) { |
| |
| // create dto type |
| val type = dto.toJvmType; |
| type.inferDtoFullState(dto, acceptor, isPrelinkingPhase) |
| |
| // create mapper type |
| if (dto.wrappedType !== null) { |
| val mapperType = dto.toMapperJvmType; |
| mapperType.inferMapperFullState(dto, acceptor, isPrelinkingPhase) |
| } |
| } |
| |
| def dispatch void inferTypesOnly(LDto dto, IJvmDeclaredTypeAcceptor acceptor, boolean isPrelinkingPhase) { |
| |
| // create dto type |
| val type = dto.toJvmType; |
| acceptor.accept(type); |
| |
| // create mapper type |
| if (dto.wrappedType !== null) { |
| val mapperType = dto.toMapperJvmType; |
| acceptor.accept(mapperType) |
| } |
| |
| // pass inferring to delegates |
| inferTypesOnlyByDelegates(dto, acceptor, isPrelinkingPhase); |
| } |
| |
| def dispatch void inferFullState(JvmGenericType type, LDto dto, IJvmDeclaredTypeAcceptor acceptor, |
| boolean isPrelinkingPhase, String selector) { |
| if (selector.equals("Mapper")) { |
| type.inferMapperFullState(dto, acceptor, isPrelinkingPhase) |
| } else { |
| type.inferDtoFullState(dto, acceptor, isPrelinkingPhase) |
| } |
| } |
| |
| def void inferDtoFullState(JvmDeclaredType type, LDto dto, IJvmDeclaredTypeAcceptor acceptor, |
| boolean isPrelinkingPhase) { |
| |
| acceptor.accept(type).initializeLater [ |
| val TimeLogger doInferLog = TimeLogger.start(getClass()) |
| abstract = dto.abstract |
| annotationCompiler.processAnnotation(dto, it) |
| var LAttribute idAttribute = null |
| var JvmField idField = null |
| var JvmField versionField = null |
| fileHeader = (dto.eContainer as LTypedPackage).documentation |
| documentation = dto.getDocumentation |
| if (dto.getSuperType !== null && !dto.getSuperType.fullyQualifiedName.toString.empty) { |
| superTypes += dto.superType.toTypeReference |
| } |
| if(dto.isHistorizedOrTimedependent) { |
| superTypes += references.getTypeForName(typeof(IDtoHistorized), dto, null) |
| } else { |
| superTypes += references.getTypeForName(typeof(IDto), dto, null) |
| } |
| superTypes += references.getTypeForName(typeof(Serializable), dto, null) |
| superTypes += references.getTypeForName(typeof(PropertyChangeListener), dto, null) |
| if (dto.getSuperType === null) { |
| members += dto.toPropertyChangeSupportField() |
| members += dto.toDiposeField |
| members += dto.toDirtyField() |
| } |
| // |
| // Fields |
| // |
| for (f : dto.getFeatures) { |
| switch f { |
| LAttribute: { |
| if (!f.derived && f.fullyQualifiedName !== null && !f.fullyQualifiedName.toString.empty) { |
| if (f.isIDorUUID) { |
| idAttribute = f |
| idField = f.toField |
| members += idField |
| } else if ((f as LDtoAbstractAttribute).versionAttr) { |
| versionField = f.toField |
| members += versionField |
| } else { |
| members += f.toField |
| } |
| } |
| } |
| LReference: { |
| if (f.fullyQualifiedName !== null && !f.fullyQualifiedName.toString.empty) { |
| members += f.toField |
| } |
| } |
| } |
| } |
| // |
| // Constructor |
| // |
| members += dto.toConstructor() [ |
| body = '''installLazyCollections();''' |
| ] |
| members += dto.toInstallLazyCollections |
| // |
| // Field accessors |
| // |
| if (dto.getSuperType === null) { |
| members += dto.toIsDisposed() |
| members += dto.toAddPropertyChangeListener() |
| members += dto.toAddPropertyChangeListenerWithProperty() |
| members += dto.toRemovePropertyChangeListener() |
| members += dto.toRemovePropertyChangeListenerWithProperty() |
| members += dto.toFirePropertyChange(versionField !== null) |
| members += dto.toIsDirty() |
| members += dto.toSetDirty() |
| } |
| members += dto.toCheckDisposed() |
| members += dto.toDispose() |
| for (f : dto.getFeatures) { |
| switch f { |
| LAttribute: { |
| members += f.toGetter() |
| if (!f.derived) { |
| if (f.isToMany) { |
| members += f.toInternalCollectionGetter(f.toName) |
| members += f.toAdder(f.toName) |
| members += f.toRemover(f.toName) |
| members += f.toInternalAdder |
| members += f.toInternalRemover |
| members += f.toSetter() |
| } else { |
| if ((f as LDtoAbstractAttribute).versionAttr) { |
| members += (f as LDtoAbstractAttribute).toVersionSetter() |
| } else { |
| members += f.toSetter() |
| } |
| } |
| } |
| } |
| LReference: { |
| members += f.toGetter() |
| if (f.isToMany) { |
| members += f.toInternalCollectionGetter(f.toName) |
| members += f.toAdder(f.toName) |
| members += f.toRemover(f.toName) |
| members += f.toInternalAdder |
| members += f.toInternalRemover |
| members += f.toSetter() |
| } else { |
| members += f.toSetter() |
| |
| if (f.isCascading || f.opposite !== null) { |
| members += f.toInternalSetter |
| } |
| } |
| } |
| } |
| } |
| // |
| // Methods. |
| // |
| for (op : dto.getOperations) { |
| members += op.toMethod(op.toName, op.getType) [ |
| documentation = op.getDocumentation |
| for (p : op.getParams) { |
| parameters += p.toParameter(p.name, p.parameterType) |
| } |
| body = op.getBody |
| ] |
| } |
| if (idAttribute !== null) { |
| members += idAttribute.toEqualVersionsMethod(it, false, idField, versionField) |
| |
| if(dto.historizedOrTimedependent) { |
| members += dto.toNewIdVersion(idField.simpleName) |
| } |
| } |
| |
| val JvmParameterizedTypeReference typeRef = TypesFactory.eINSTANCE.createJvmParameterizedTypeReference |
| typeRef.type = it |
| |
| members += dto.toMethod("propertyChange", references.getTypeForName(Void::TYPE, dto)) [ |
| parameters += dto.toParameter("event", newTypeRef(typeof(PropertyChangeEvent).name, null)) |
| body = ''' |
| Object source = event.getSource(); |
| |
| // forward the event from embeddable beans to all listeners. So the parent of the embeddable |
| // bean will become notified and its dirty state can be handled properly |
| «FOR ref : dto.features.filter[!(it instanceof LOperation)].filter[it.toRawType.isBean]» |
| |
| |
| if(source == «ref.toName»){ |
| firePropertyChange("«ref.toName»" + "_" + event.getPropertyName(), event.getOldValue(), event.getNewValue()); |
| } else |
| «ENDFOR» |
| { |
| «IF dto.superType !== null» |
| super.propertyChange(event); |
| «ELSE» |
| // no super class available to forward event |
| «ENDIF» |
| } |
| ''' |
| ] |
| doInferLog.stop(log, "Inferring dto " + dto.name) |
| ] |
| } |
| |
| def void inferMapperFullState(JvmGenericType type, LDto dto, IJvmDeclaredTypeAcceptor acceptor, |
| boolean isPrelinkingPhase) { |
| |
| acceptor.accept(type).initializeLater [ |
| val TimeLogger doInferLog = TimeLogger.start(getClass()); |
| fileHeader = (dto.eContainer as LTypedPackage).documentation |
| documentation = ''' |
| This class maps the dto {@link «dto.toName»} to and from the entity {@link «dto.wrappedType.toName»}. |
| ''' |
| // |
| // Constructor |
| // |
| members += dto.toConstructor()[] |
| if (dto.wrappedType !== null) { |
| val dtoParam = TypesFactory.eINSTANCE.createJvmTypeParameter |
| dtoParam.name = "DTO" |
| val dtoUpper = TypesFactory.eINSTANCE.createJvmUpperBound |
| |
| dtoUpper.typeReference = dto.findDtoTypeReference |
| dtoParam.constraints += dtoUpper |
| typeParameters += dtoParam |
| |
| val entityParam = TypesFactory.eINSTANCE.createJvmTypeParameter |
| entityParam.name = "ENTITY" |
| val entityUpper = TypesFactory.eINSTANCE.createJvmUpperBound |
| entityUpper.typeReference = dto.wrappedType?.toTypeReference |
| entityParam.constraints += entityUpper |
| typeParameters += entityParam |
| |
| val entityType = TypesFactory.eINSTANCE.createJvmParameterizedTypeReference; |
| entityType.type = entityParam |
| |
| val dtoType = TypesFactory.eINSTANCE.createJvmParameterizedTypeReference; |
| dtoType.type = dtoParam |
| if (dto.getSuperType !== null) { |
| superTypes += dto.findSuperDtoMapperType(dtoType, entityType) |
| } else { |
| superTypes += references.getTypeForName(typeof(IMapper), dto, dtoType, entityType) |
| members += dto.toField("mapperAccess", references.getTypeForName(typeof(IMapperAccess), dto, null)) |
| members += dto.toGetToDtoMapperAccess |
| members += dto.toGetToEntityMapperAccess |
| |
| members += dto.toMapperBindMethod |
| members += dto.toMapperUnbindMethod |
| } |
| |
| members += dto.toMethod("createEntity", dto.wrappedType.toTypeReference) [ |
| documentation = '''Creates a new instance of the entity''' |
| body = ''' |
| «IF dto.wrappedType.abstract»throw new UnsupportedOperationException("Subclass needs to provide dto.");«ELSE»return new «dto. |
| wrappedType.toName»();«ENDIF» |
| ''' |
| ] |
| |
| members += dto.toMethod("createDto", dto.findDtoTypeReference) [ |
| documentation = '''Creates a new instance of the dto''' |
| body = ''' |
| «IF dto.abstract»throw new UnsupportedOperationException("Subclass needs to provide dto.");«ELSE»return new «dto. |
| toName»();«ENDIF» |
| ''' |
| ] |
| |
| members += dto.toMapToDto |
| members += dto.toMapToEntity |
| |
| for (f : dto.getFeatures) { |
| switch f { |
| case f instanceof LDtoAbstractAttribute: { |
| val LDtoAbstractAttribute att = f as LDtoAbstractAttribute |
| if (att.inherited || att.mapper?.toDTO !== null) { |
| members += att.toMapToDtoProperty |
| } |
| if (att.inherited || att.mapper?.fromDTO !== null) { |
| members += att.toMapToEntityProperty |
| } |
| } |
| case f instanceof LDtoAbstractReference: { |
| val LDtoAbstractReference att = f as LDtoAbstractReference |
| if (att.inherited || att.mapper?.toDTO !== null) { |
| members += att.toMapToDtoProperty |
| } |
| |
| if (att.inherited || att.mapper?.fromDTO !== null) |
| members += att.toMapToEntityProperty |
| } |
| } |
| } |
| |
| val idAtt = dto.findIdProperty |
| members += dto.toMethod("createDtoHash", references.getTypeForName(typeof(String), dto, null)) [ |
| parameters += dto.toParameter("in", references.getTypeForName(typeof(Object), dto, null)) |
| if (idAtt !== null) { |
| body = ''' |
| return org.eclipse.osbp.runtime.common.hash.HashUtil.createObjectWithIdHash(«dto.toName».class, in); |
| ''' |
| } else { |
| body = ''' |
| throw new UnsupportedOperationException("No id attribute available"); |
| ''' |
| } |
| ] |
| |
| members += dto.toMethod("createEntityHash", references.getTypeForName(typeof(String), dto, null)) [ |
| parameters += dto.toParameter("in", references.getTypeForName(typeof(Object), dto, null)) |
| if (idAtt !== null) { |
| body = ''' |
| return org.eclipse.osbp.runtime.common.hash.HashUtil.createObjectWithIdHash(«dto.wrappedType.toName».class, in); |
| ''' |
| } else { |
| body = ''' |
| throw new UnsupportedOperationException("No id attribute available"); |
| ''' |
| } |
| ] |
| |
| } |
| doInferLog.stop(log, "Inferring mapper " + dto.name) |
| ] |
| } |
| |
| def LAttribute findIdProperty(LDto dto) { |
| for (att : dto.allFeatures.filter(typeof(LAttribute)).map[it as LAttribute]) { |
| if (att.isIDorUUID) { |
| return att |
| } |
| } |
| return null |
| } |
| |
| // used for test cases with old derived state computer |
| def dispatch void infer(LEnum enumX, IJvmDeclaredTypeAcceptor acceptor, boolean isPrelinkingPhase) { |
| |
| // create dto type |
| val type = enumX.toEnumerationType(enumX.fullyQualifiedName.toString, null) |
| type.inferFullState(enumX, acceptor, isPrelinkingPhase, "") |
| } |
| |
| def dispatch void inferTypesOnly(LEnum enumX, IJvmDeclaredTypeAcceptor acceptor, boolean isPrelinkingPhase) { |
| |
| // create dto type |
| val type = enumX.toEnumerationType(enumX.fullyQualifiedName.toString, null) |
| acceptor.accept(type); |
| |
| // pass inferring to delegates |
| inferTypesOnlyByDelegates(enumX, acceptor, isPrelinkingPhase); |
| } |
| |
| def dispatch void inferFullState(JvmDeclaredType type, LEnum enumX, IJvmDeclaredTypeAcceptor acceptor, |
| boolean isPrelinkingPhase, String selector) { |
| |
| acceptor.accept(type).initializeLater [ |
| val TimeLogger doInferLog = TimeLogger.start(getClass()); |
| fileHeader = (enumX.eContainer as LTypedPackage).documentation |
| documentation = enumX.documentation |
| for (f : enumX.literals) { |
| documentation = f.documentation |
| members += f.toEnumerationLiteral(f.name) |
| } |
| doInferLog.stop(log, "Inferring enum " + enumX.name) |
| ] |
| } |
| } |