| /** |
| * 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.validation; |
| |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import javax.persistence.Persistence; |
| import javax.persistence.PostPersist; |
| import javax.persistence.PostRemove; |
| import javax.persistence.PostUpdate; |
| import javax.persistence.PrePersist; |
| import javax.persistence.PreRemove; |
| import javax.persistence.PreUpdate; |
| |
| import org.eclipse.emf.ecore.EAttribute; |
| import org.eclipse.emf.ecore.util.EcoreUtil; |
| import org.eclipse.osbp.dsl.entity.xtext.extensions.ModelExtensions; |
| import org.eclipse.osbp.dsl.entity.xtext.util.PersistenceNamingUtils; |
| import org.eclipse.osbp.dsl.semantic.common.helper.Bounds; |
| import org.eclipse.osbp.dsl.semantic.common.types.LDataType; |
| import org.eclipse.osbp.dsl.semantic.common.types.LEnum; |
| import org.eclipse.osbp.dsl.semantic.common.types.LFeature; |
| import org.eclipse.osbp.dsl.semantic.common.types.LPackage; |
| 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.dsl.semantic.common.types.LTypedPackage; |
| import org.eclipse.osbp.dsl.semantic.common.types.OSBPTypesPackage; |
| import org.eclipse.osbp.dsl.semantic.entity.LBean; |
| 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.LEntityFeature; |
| import org.eclipse.osbp.dsl.semantic.entity.LEntityInheritanceStrategy; |
| import org.eclipse.osbp.dsl.semantic.entity.LEntityModel; |
| import org.eclipse.osbp.dsl.semantic.entity.LEntityPersistenceInfo; |
| import org.eclipse.osbp.dsl.semantic.entity.LEntityReference; |
| import org.eclipse.osbp.dsl.semantic.entity.LEntitySuperIndex; |
| import org.eclipse.osbp.dsl.semantic.entity.LIndex; |
| import org.eclipse.osbp.dsl.semantic.entity.LTablePerClassStrategy; |
| import org.eclipse.osbp.dsl.semantic.entity.LTablePerSubclassStrategy; |
| import org.eclipse.osbp.dsl.semantic.entity.OSBPEntityPackage; |
| import org.eclipse.osbp.preferences.EnumDatabaseVendor; |
| import org.eclipse.xtext.common.types.JvmAnnotationReference; |
| import org.eclipse.xtext.common.types.JvmGenericType; |
| import org.eclipse.xtext.common.types.JvmOperation; |
| import org.eclipse.xtext.common.types.TypesPackage; |
| import org.eclipse.xtext.common.types.util.TypeReferences; |
| import org.eclipse.xtext.naming.IQualifiedNameProvider; |
| import org.eclipse.xtext.resource.IContainer; |
| import org.eclipse.xtext.resource.IEObjectDescription; |
| import org.eclipse.xtext.resource.IResourceDescription; |
| import org.eclipse.xtext.resource.IResourceDescriptions; |
| import org.eclipse.xtext.resource.impl.ResourceDescriptionsProvider; |
| import org.eclipse.xtext.validation.Check; |
| import org.eclipse.xtext.validation.CheckType; |
| import org.eclipse.xtext.validation.NamesAreUniqueValidator; |
| import org.eclipse.xtext.validation.ValidationMessageAcceptor; |
| import org.eclipse.xtext.xbase.jvmmodel.IJvmModelAssociations; |
| import org.eclipse.xtext.xbase.lib.Extension; |
| |
| import com.google.inject.Inject; |
| |
| /** |
| * Custom validation rules. |
| * |
| * see http://www.eclipse.org/Xtext/documentation.html#validation |
| */ |
| @SuppressWarnings("restriction") |
| public class EntityGrammarValidator extends AbstractEntityGrammarValidator { |
| |
| public static final String CODE__DIFFERING_INHERITANCE_FROM_SUPERTYPE = "104"; |
| public static final String CODE__INHERITANCE_PROPERTY_IGNORED = "105"; |
| public static final String CODE__INHERITANCE_DISCRIMINATOR_VALUE_NOT_UNIQUE = "106"; |
| public static final String CODE__DUPLICATE_PERSISTENCE = "106"; |
| public static final String CODE__DUPLICATE_ID = "107"; |
| public static final String CODE__DUPLICATE_VERSION = "108"; |
| public static final String CODE__MISSING_ID = "109"; |
| public static final String CODE__DUPLICATE_PROPERTY_NAME = "110"; |
| public static final String CODE__MISSING_OPPOSITE_REFERENCE = "111"; |
| public static final String CODE__BIDIRECTIONAL_CASCADE_INVALID = "112"; |
| public static final String CODE__CASCADE_DIRECTION_INVALID = "113"; |
| public static final String CODE__UUID_WRONG_TYPE = "114"; |
| public static final String CODE__OPPOSITE_WITHOUT_CASCADE = "115"; |
| public static final String CODE__MISSING_ID_FOR_VERSIONED = "116"; |
| public static final String CODE__HISTORIZED_IN_SUBCLASS = "117"; |
| public static final String CODE__TIMEDEPENDENT_IN_SUBCLASS = "118"; |
| private static final String CODE__DUPLICATE_DOMAIN_KEY = "119"; |
| private static final String CODE__DUPLICATE_DOMAIN_DESCRIPTION = "120"; |
| private static final String CODE__DOMAIN_KEY__NO_MANY = "121"; |
| private static final String CODE__DOMAIN_DESCRIPTION__NO_MANY = "122"; |
| private static final String CODE__DOMAIN_KEY__TYPE = "123"; |
| private static final String CODE__DOMAIN_DESCRIPTION__TYPE = "124"; |
| private static final String CODE__BEAN_TO_ENTITY__NO_MANY_RELATION_SUPPORTED = "125"; |
| private static final String CODE__BEAN_TO_ENTITY__NO_OPPOSITE_RELATION_SUPPORTED = "126"; |
| private static final String CODE__ENTITY_TO_BEAN__NO_OPPOSITE_RELATION_SUPPORTED = "127"; |
| private static final String CODE__DUPLICATE__PERSISTENCE_ANNOTATION = "128"; |
| public static final String CODE__INHERITANCE_NO_SUB_TYPES = "129"; |
| |
| @Inject |
| private IQualifiedNameProvider qnp; |
| @Inject |
| private ModelExtensions extensions; |
| @Inject |
| private NamesAreUniqueValidator uniqueValidator; |
| @Inject |
| private IContainer.Manager containermanager; |
| @Inject |
| private ResourceDescriptionsProvider resourceDescriptionsProvider; |
| @Inject |
| private IJvmModelAssociations modelAssociations; |
| |
| @Check |
| public void checkDatatype_asPrimitive(LDataType dt) { |
| super.checkDatatype_asPrimitive(dt); |
| } |
| |
| @Check |
| public void checkJPA_MultiHasOppositeReference(LEntityReference prop) { |
| if (extensions.isToMany(prop)) { |
| if (prop.getOpposite() == null) { |
| error("A 'to-many' association needs an opposite reference.", |
| OSBPEntityPackage.Literals.LENTITY_REFERENCE__OPPOSITE, |
| ValidationMessageAcceptor.INSIGNIFICANT_INDEX, CODE__MISSING_OPPOSITE_REFERENCE, |
| (String[]) null); |
| } else if (prop.getOpposite().getOpposite() == null) { |
| error("Missing opposite reference", prop.getOpposite(), OSBPTypesPackage.Literals.LFEATURE__NAME, |
| ValidationMessageAcceptor.INSIGNIFICANT_INDEX, CODE__MISSING_OPPOSITE_REFERENCE, |
| (String[]) null); |
| } |
| } |
| } |
| |
| @Check |
| public void checkBean_MultiHasOppositeReference(LBeanReference prop) { |
| if (prop.getType() instanceof LEntity) { |
| if (extensions.isToMany(prop)) { |
| error("To-Many-Relations are not supported for bean->entity references.", |
| OSBPTypesPackage.Literals.LFEATURE__MULTIPLICITY, ValidationMessageAcceptor.INSIGNIFICANT_INDEX, |
| CODE__BEAN_TO_ENTITY__NO_MANY_RELATION_SUPPORTED, (String[]) null); |
| } |
| if (prop.getOpposite() != null) { |
| error("Opposite-Relations are not supported for bean->entity references.", |
| OSBPEntityPackage.Literals.LBEAN_REFERENCE__OPPOSITE, |
| ValidationMessageAcceptor.INSIGNIFICANT_INDEX, |
| CODE__BEAN_TO_ENTITY__NO_OPPOSITE_RELATION_SUPPORTED, (String[]) null); |
| } |
| } |
| } |
| |
| @Check |
| public void checkEntity_MultiHasOppositeReference(LEntityAttribute prop) { |
| if (prop.getType() instanceof LBean) { |
| if (prop.getOpposite() != null) { |
| error("Opposite-Relations are not supported for embeddables.", |
| OSBPEntityPackage.Literals.LENTITY_ATTRIBUTE__OPPOSITE, |
| ValidationMessageAcceptor.INSIGNIFICANT_INDEX, |
| CODE__ENTITY_TO_BEAN__NO_OPPOSITE_RELATION_SUPPORTED, (String[]) null); |
| } |
| } |
| } |
| |
| @Check |
| public void checkJPA_OppositeNotAlsoCascadeMergePersist(LEntityReference prop) { |
| if (prop.getOpposite() != null) { |
| if (prop.isCascadeMergePersist() && prop.getOpposite().isCascadeMergePersist()) { |
| error("Only one opposite may be specified as cascade", |
| OSBPTypesPackage.Literals.LFEATURE__CASCADE_MERGE_PERSIST, CODE__BIDIRECTIONAL_CASCADE_INVALID, |
| (String[]) null); |
| } |
| |
| if (extensions.isToMany(prop.getOpposite())) { |
| if (prop.isCascadeMergePersist()) { |
| error("Cascade must not affect the common parent in a many-to-one relation", prop, |
| OSBPTypesPackage.Literals.LFEATURE__CASCADE_MERGE_PERSIST, CODE__CASCADE_DIRECTION_INVALID, |
| new String[0]); |
| } |
| } |
| } |
| } |
| |
| @Check |
| public void checkJPA_OppositeNotAlsoCascadeRemove(LEntityReference prop) { |
| if (prop.getOpposite() != null) { |
| if (prop.isCascadeRemove() && prop.getOpposite().isCascadeRemove()) { |
| error("Only one opposite may be specified as cascade", |
| OSBPTypesPackage.Literals.LFEATURE__CASCADE_REMOVE, CODE__BIDIRECTIONAL_CASCADE_INVALID, |
| (String[]) null); |
| } |
| |
| if (extensions.isToMany(prop.getOpposite())) { |
| if (prop.isCascadeRemove()) { |
| error("Cascade must not affect the common parent in a many-to-one relation", prop, |
| OSBPTypesPackage.Literals.LFEATURE__CASCADE_REMOVE, CODE__CASCADE_DIRECTION_INVALID, |
| new String[0]); |
| } |
| } |
| } |
| } |
| |
| @Check |
| public void checkJPA_Opposite_OneIsCascadeMergePersist(LEntityReference prop) { |
| Bounds propBound = extensions.getBounds(prop); |
| Bounds oppositeBound = extensions.getBounds(prop.getOpposite()); |
| |
| if (propBound.isToMany() || oppositeBound.isToMany()) { |
| // no check required! |
| return; |
| } |
| |
| if (prop.getOpposite() != null) { |
| if (!prop.isCascadeMergePersist() && !prop.getOpposite().isCascadeMergePersist()) { |
| error("Opposite references may only defined for cascading relations.", prop, |
| OSBPTypesPackage.Literals.LFEATURE__CASCADE_MERGE_PERSIST, CODE__OPPOSITE_WITHOUT_CASCADE, |
| new String[0]); |
| } |
| } |
| } |
| |
| @Check |
| public void checkJPA_Opposite_OneIsCascadeRemove(LEntityReference prop) { |
| Bounds propBound = extensions.getBounds(prop); |
| Bounds oppositeBound = extensions.getBounds(prop.getOpposite()); |
| |
| if (propBound.isToMany() || oppositeBound.isToMany()) { |
| // no check required! |
| return; |
| } |
| |
| if (prop.getOpposite() != null) { |
| if (!prop.isCascadeRemove() && !prop.getOpposite().isCascadeRemove()) { |
| error("Opposite references may only defined for cascading relations.", prop, |
| OSBPTypesPackage.Literals.LFEATURE__CASCADE_REMOVE, CODE__OPPOSITE_WITHOUT_CASCADE, |
| new String[0]); |
| } |
| } |
| } |
| |
| @Check |
| public void checkBean_OppositeNotAlsoCascadeMergePersist(LBeanReference prop) { |
| if (prop.getType() instanceof LBean) { |
| if (prop.getOpposite() != null) { |
| if (prop.isCascadeMergePersist() && isCascading(prop)) { |
| error("Only one opposite may be specified as cascade", |
| OSBPTypesPackage.Literals.LFEATURE__CASCADE_MERGE_PERSIST, |
| CODE__BIDIRECTIONAL_CASCADE_INVALID, (String[]) null); |
| } |
| |
| if (extensions.isToMany(prop.getOpposite())) { |
| if (prop.isCascadeMergePersist()) { |
| error("Cascade must not affect the common parent in a many-to-one relation", prop, |
| OSBPTypesPackage.Literals.LFEATURE__CASCADE_MERGE_PERSIST, |
| CODE__CASCADE_DIRECTION_INVALID, new String[0]); |
| } |
| } |
| } |
| } |
| } |
| |
| @Check |
| public void checkBean_OppositeNotAlsoCascadeRemove(LBeanReference prop) { |
| if (prop.getType() instanceof LBean) { |
| if (prop.getOpposite() != null) { |
| if (prop.isCascadeRemove() && isCascading(prop)) { |
| error("Only one opposite may be specified as cascade", |
| OSBPTypesPackage.Literals.LFEATURE__CASCADE_REMOVE, CODE__BIDIRECTIONAL_CASCADE_INVALID, |
| (String[]) null); |
| } |
| |
| if (extensions.isToMany(prop.getOpposite())) { |
| if (prop.isCascadeRemove()) { |
| error("Cascade must not affect the common parent in a many-to-one relation", prop, |
| OSBPTypesPackage.Literals.LFEATURE__CASCADE_REMOVE, CODE__CASCADE_DIRECTION_INVALID, |
| new String[0]); |
| } |
| } |
| } |
| } |
| } |
| |
| protected boolean isCascading(LBeanReference prop) { |
| LFeature opposite = prop.getOpposite(); |
| if (opposite instanceof LReference) { |
| return ((LReference) opposite).isCascadeMergePersist() || ((LReference) opposite).isCascadeRemove(); |
| } else { |
| return false; |
| } |
| } |
| |
| @Check |
| public void checkProperties_JavaKeyWord(LFeature lprop) { |
| super.checkProperties_JavaKeyWord(lprop); |
| } |
| |
| @Check |
| public void checkDuplicatePackages_InFile(LEntityModel lmodel) { |
| Set<String> names = new HashSet<String>(); |
| int counter = -1; |
| for (LPackage pkg : lmodel.getPackages()) { |
| counter++; |
| String pkgName = qnp.getFullyQualifiedName(pkg).toString(); |
| if (names.contains(pkgName)) { |
| error(String.format("Package %s must not be defined twice!", pkgName), |
| OSBPEntityPackage.Literals.LENTITY_MODEL__PACKAGES, counter, CODE__DUPLICATE_LPACKAGE_IN_FILE, |
| (String[]) null); |
| } |
| names.add(pkgName); |
| } |
| } |
| |
| @Check(CheckType.NORMAL) |
| public void checkDuplicateType_InProject(LType type) { |
| if (type instanceof LDataType) { |
| return; |
| } |
| super.checkDuplicateType_InProject(type); |
| } |
| |
| @Check(CheckType.NORMAL) |
| public void checkDuplicateDatatypeInPackage(LTypedPackage pkg) { |
| super.checkDuplicateDatatypeInPackage(pkg); |
| } |
| |
| @Check(CheckType.NORMAL) |
| public void checkDuplicatePackage_InProject(LPackage lPackage) { |
| super.checkDuplicatePackage_InProject(lPackage); |
| } |
| |
| @Check |
| public void checkBeanManyToMany(LBeanReference prop) { |
| ModelExtensions extension = new ModelExtensions(); |
| if (prop.getOpposite() != null && extension.isToMany(prop) && extension.isToMany(prop.getOpposite())) { |
| error(String.format("ManyToMany relations are not permitted!", qnp.getFullyQualifiedName(prop).toString()), |
| OSBPEntityPackage.Literals.LBEAN_REFERENCE__OPPOSITE, ValidationMessageAcceptor.INSIGNIFICANT_INDEX, |
| CODE__MANY_TO_MANY__NOT_SUPPORTED, (String[]) null); |
| } |
| } |
| |
| @Check |
| public void checkManyToMany(LEntityReference prop) { |
| ModelExtensions extension = new ModelExtensions(); |
| if (prop.getOpposite() != null && extension.isToMany(prop) && extension.isToMany(prop.getOpposite())) { |
| error(String.format("ManyToMany relations are not permitted!", qnp.getFullyQualifiedName(prop).toString()), |
| OSBPEntityPackage.Literals.LENTITY_REFERENCE__OPPOSITE, |
| ValidationMessageAcceptor.INSIGNIFICANT_INDEX, CODE__MANY_TO_MANY__NOT_SUPPORTED, (String[]) null); |
| } |
| } |
| |
| @Check(CheckType.NORMAL) |
| public void checkJPA_ConsistentInheritanceStrategy(LEntity entity) { |
| // no checks required - inheritance is inherited |
| if (entity.getInheritanceStrategy() == null) { |
| return; |
| } |
| |
| LEntityInheritanceStrategy differingSuperStgy = searchDifferingSuperStrategy(entity.getInheritanceStrategy(), |
| entity); |
| if (differingSuperStgy != null) { |
| LEntity superType = (LEntity) differingSuperStgy.eContainer(); |
| warning(String.format( |
| "The supertype %s uses the inheritance strategy %s. The inheritance of this entity is ignored!", |
| qnp.getFullyQualifiedName(superType).toString(), getStrategyName(differingSuperStgy)), |
| OSBPEntityPackage.Literals.LENTITY__INHERITANCE_STRATEGY, |
| ValidationMessageAcceptor.INSIGNIFICANT_INDEX, CODE__DIFFERING_INHERITANCE_FROM_SUPERTYPE, |
| (String[]) null); |
| } |
| } |
| |
| @Check(CheckType.NORMAL) |
| public void checkJPA_Features(LEntityAttribute prop) { |
| if (prop.isUuid()) { |
| boolean typeOK = false; |
| if (prop.getType() instanceof LDataType) { |
| LDataType type = (LDataType) prop.getType(); |
| String typename = type.getJvmTypeReference().getQualifiedName(); |
| if (typename.equals("java.lang.String")) { |
| typeOK = true; |
| } |
| } |
| |
| if (!typeOK) { |
| error("UUIDs must be of type String.", OSBPTypesPackage.Literals.LATTRIBUTE__UUID, |
| CODE__UUID_WRONG_TYPE, new String[0]); |
| } |
| } |
| |
| if (prop.isDomainKey()) { |
| if (extensions.isToMany(prop)) { |
| error("DomainKey is not valid for one to many relations.", |
| OSBPTypesPackage.Literals.LATTRIBUTE__DOMAIN_KEY, CODE__DOMAIN_KEY__NO_MANY, new String[0]); |
| } |
| |
| if (prop.getType() instanceof LDataType) { |
| LDataType type = (LDataType) prop.getType(); |
| if (type.getJvmTypeReference() == null |
| || !"java.lang.String".equals(type.getJvmTypeReference().getQualifiedName())) { |
| error("DomainKey must be of type String.", OSBPTypesPackage.Literals.LATTRIBUTE__DOMAIN_KEY, |
| CODE__DOMAIN_KEY__TYPE, new String[0]); |
| } |
| } |
| } |
| |
| if (prop.isDomainDescription()) { |
| if (extensions.isToMany(prop)) { |
| error("DomainDescription is not valid for one to many relations.", |
| OSBPTypesPackage.Literals.LATTRIBUTE__DOMAIN_DESCRIPTION, CODE__DOMAIN_DESCRIPTION__NO_MANY, |
| new String[0]); |
| } |
| |
| if (prop.getType() instanceof LDataType) { |
| LDataType type = (LDataType) prop.getType(); |
| String typename = type.getJvmTypeReference().getQualifiedName(); |
| if (!typename.equals("java.lang.String")) { |
| error("DomainDescription must be of type String.", |
| OSBPTypesPackage.Literals.LATTRIBUTE__DOMAIN_DESCRIPTION, CODE__DOMAIN_DESCRIPTION__TYPE, |
| new String[0]); |
| } |
| } |
| } |
| } |
| |
| @Check(CheckType.NORMAL) |
| public void checkJPA_Historized(LEntity entity) { |
| if (entity.isHistorized()) { |
| if (entity.getSuperType() != null && !entity.getSuperType().isMappedSuperclass()) { |
| error("Keyword historized may only be used in toplevel entities of inheritance hierarchy", |
| OSBPEntityPackage.Literals.LENTITY__HISTORIZED, CODE__HISTORIZED_IN_SUBCLASS, new String[0]); |
| } |
| } |
| |
| if (entity.isTimedependent()) { |
| if (entity.getSuperType() != null && !entity.getSuperType().isMappedSuperclass()) { |
| error("Keyword timedependent may only be used in toplevel entities of inheritance hierarchy", |
| OSBPEntityPackage.Literals.LENTITY__TIMEDEPENDENT, CODE__TIMEDEPENDENT_IN_SUBCLASS, |
| new String[0]); |
| } |
| } |
| } |
| |
| @Check(CheckType.NORMAL) |
| public void checkIndex_Features(LIndex index) { |
| } |
| |
| @Check(CheckType.NORMAL) |
| public void checkEntity_Index(LEntity entity) { |
| } |
| |
| @Check(CheckType.NORMAL) |
| public void checkJPA_Features(LEntity entity) { |
| |
| int idCounter = 0; |
| int versionCounter = 0; |
| int domainKeyCounter = 0; |
| int domainDescriptionCounter = 0; |
| Map<String, Integer> attNames = new HashMap<String, Integer>(); |
| for (LEntityFeature feature : entity.getAllFeatures()) { |
| if (feature instanceof LEntityAttribute) { |
| LEntityAttribute att = (LEntityAttribute) feature; |
| if (att.isId() || att.isUuid()) { |
| idCounter++; |
| } |
| if (att.isVersion()) { |
| versionCounter++; |
| } |
| if (att.isDomainKey()) { |
| domainKeyCounter++; |
| } |
| if (att.isDomainDescription()) { |
| domainDescriptionCounter++; |
| } |
| } |
| |
| if (!attNames.containsKey(feature.getName())) { |
| attNames.put(feature.getName(), 1); |
| } else { |
| int value = attNames.get(feature.getName()); |
| attNames.put(feature.getName(), ++value); |
| } |
| } |
| |
| if (idCounter == 0 && (entity.isHistorized() || entity.isTimedependent())) { |
| error("An historized or timedependent entity must have an ID property", |
| OSBPEntityPackage.Literals.LENTITY__FEATURES, CODE__MISSING_ID_FOR_VERSIONED); |
| } else |
| |
| if (idCounter == 0) { |
| warning("An entity should have an ID property", OSBPEntityPackage.Literals.LENTITY__FEATURES, |
| CODE__MISSING_ID); |
| } else if (idCounter > 1) { |
| int i = 0; |
| for (LEntityFeature feature : entity.getFeatures()) { |
| if (feature instanceof LEntityAttribute) { |
| if (((LEntityAttribute) feature).isId() || ((LEntityAttribute) feature).isUuid()) { |
| error("An entity must only have one ID property.", OSBPEntityPackage.Literals.LENTITY__FEATURES, |
| i, CODE__DUPLICATE_ID, new String[0]); |
| break; |
| } |
| } |
| |
| i++; |
| } |
| } |
| if (versionCounter > 1) { |
| int i = 0; |
| for (LEntityFeature feature : entity.getFeatures()) { |
| if (feature instanceof LEntityAttribute) { |
| if (((LEntityAttribute) feature).isVersion()) { |
| error("An entity must only have one Version property.", |
| OSBPEntityPackage.Literals.LENTITY__FEATURES, i, CODE__DUPLICATE_VERSION, |
| new String[0]); |
| break; |
| } |
| } |
| i++; |
| } |
| } |
| |
| if (domainKeyCounter > 1) { |
| int i = 0; |
| for (LEntityFeature feature : entity.getFeatures()) { |
| if (feature instanceof LEntityAttribute) { |
| if (((LEntityAttribute) feature).isDomainKey()) { |
| error("An entity must only have one DomainKey property.", |
| OSBPEntityPackage.Literals.LENTITY__FEATURES, i, CODE__DUPLICATE_DOMAIN_KEY, |
| new String[0]); |
| break; |
| } |
| } |
| i++; |
| } |
| } |
| |
| if (domainDescriptionCounter > 1) { |
| int i = 0; |
| for (LEntityFeature feature : entity.getFeatures()) { |
| if (feature instanceof LEntityAttribute) { |
| if (((LEntityAttribute) feature).isDomainDescription()) { |
| error("An entity must only have one DomainDescription property.", |
| OSBPEntityPackage.Literals.LENTITY__FEATURES, i, CODE__DUPLICATE_DOMAIN_DESCRIPTION, |
| new String[0]); |
| break; |
| } |
| } |
| i++; |
| } |
| } |
| |
| for (Map.Entry<String, Integer> entry : attNames.entrySet()) { |
| if (entry.getValue() > 1) { |
| int i = 0; |
| for (LEntityFeature feature : entity.getFeatures()) { |
| if (feature.getName().equals(entry.getKey())) { |
| error(String.format("The property \"%s\" must only be defined once!", feature.getName()), |
| OSBPEntityPackage.Literals.LENTITY__FEATURES, i, CODE__DUPLICATE_PROPERTY_NAME, |
| new String[0]); |
| break; |
| } |
| i++; |
| } |
| } |
| } |
| } |
| |
| protected String getStrategyName(LEntityInheritanceStrategy stgy) { |
| if (LTablePerClassStrategy.class.isAssignableFrom(stgy.getClass())) { |
| return "Table-Per-Class"; |
| } else { |
| return "Table-Per-Subclass"; |
| } |
| } |
| |
| protected LEntityInheritanceStrategy searchDifferingSuperStrategy(LEntityInheritanceStrategy stgy, LEntity entity) { |
| LEntity superEntity = entity.getSuperType(); |
| if (superEntity == null) { |
| return null; |
| } |
| |
| LEntityInheritanceStrategy superStgy = superEntity.getInheritanceStrategy(); |
| if (superStgy == null) { |
| return searchDifferingSuperStrategy(stgy, superEntity); |
| } |
| |
| if (!stgy.getClass().getName().equals(superStgy.getClass().getName())) { |
| return superStgy; |
| } |
| |
| return searchDifferingSuperStrategy(stgy, superEntity); |
| } |
| |
| @Check(CheckType.NORMAL) |
| public void checkJPA_IgnoredInheritanceStrategyProperties(LEntity entity) { |
| // no checks required - inheritance is inherited |
| LEntityInheritanceStrategy stgy = entity.getInheritanceStrategy(); |
| if (stgy == null) { |
| return; |
| } |
| |
| if (entity.getSuperType() != null && !extensions.checkIsMappedSuperclass(entity.getSuperType())) { |
| if (LTablePerClassStrategy.class.isAssignableFrom(stgy.getClass())) { |
| LTablePerClassStrategy castStgy = (LTablePerClassStrategy) stgy; |
| if (castStgy.getDiscriminatorColumn() != null) { |
| sendIgnoredInheritancePropertyWarning(stgy, |
| OSBPEntityPackage.Literals.LTABLE_PER_CLASS_STRATEGY__DISCRIMINATOR_COLUMN); |
| } |
| if (castStgy.getDiscriminatorType() != LDiscriminatorType.INHERIT) { |
| sendIgnoredInheritancePropertyWarning(stgy, |
| OSBPEntityPackage.Literals.LTABLE_PER_CLASS_STRATEGY__DISCRIMINATOR_TYPE); |
| } |
| } else { |
| LTablePerSubclassStrategy castStgy = (LTablePerSubclassStrategy) stgy; |
| if (castStgy.getDiscriminatorColumn() != null) { |
| sendIgnoredInheritancePropertyWarning(stgy, |
| OSBPEntityPackage.Literals.LTABLE_PER_SUBCLASS_STRATEGY__DISCRIMINATOR_COLUMN); |
| } |
| if (castStgy.getDiscriminatorType() != LDiscriminatorType.INHERIT) { |
| sendIgnoredInheritancePropertyWarning(stgy, |
| OSBPEntityPackage.Literals.LTABLE_PER_SUBCLASS_STRATEGY__DISCRIMINATOR_TYPE); |
| } |
| } |
| } |
| } |
| |
| @Check(CheckType.NORMAL) |
| public void checkJPA_InheritanceStrategy_NotUniqueDiscriminatorValue(LEntity entity) { |
| if (entity.getSuperType() == null) { |
| return; |
| } |
| |
| String currentValue = extensions.toDiscriminatorValue(extensions.toInheritanceStrategy(entity)); |
| |
| // collect all super type strategies |
| List<LEntityInheritanceStrategy> stgies = extensions.collectAllInheritanceStrategies(entity.getSuperType()); |
| |
| for (LEntityInheritanceStrategy stgy : stgies) { |
| String value = extensions.toDiscriminatorValue(stgy); |
| if (value.equals(currentValue)) { |
| error(String.format("The discrimator value %s is already used by supertype!", value), entity, |
| OSBPEntityPackage.Literals.LENTITY__INHERITANCE_STRATEGY, |
| ValidationMessageAcceptor.INSIGNIFICANT_INDEX, CODE__INHERITANCE_DISCRIMINATOR_VALUE_NOT_UNIQUE, |
| (String[]) null); |
| } |
| } |
| } |
| |
| @Check(CheckType.NORMAL) |
| public void checkJPA_InheritanceStrategy(LEntity entity) { |
| LEntityInheritanceStrategy stgy = entity.getInheritanceStrategy(); |
| if (stgy == null) { |
| return; |
| } |
| |
| if (entity.getSubTypes().isEmpty()) { |
| error("Inheritance is only allowed for entities with sub types.", entity, |
| OSBPEntityPackage.Literals.LENTITY__INHERITANCE_STRATEGY, |
| ValidationMessageAcceptor.INSIGNIFICANT_INDEX, CODE__INHERITANCE_NO_SUB_TYPES, (String[]) null); |
| } |
| } |
| |
| @Check(CheckType.NORMAL) |
| public void checkFilteringAndRange(LEntityAttribute attribute) { |
| if (attribute.isFiltering() || attribute.isRangeFiltering()) { |
| LScalarType type = attribute.getType(); |
| if (type instanceof LDataType) { |
| LDataType dt = (LDataType) type; |
| if (!dt.isAsBlob() && !dt.isSyntheticFlag()) { |
| return; |
| } |
| } |
| if (attribute.isFiltering()) { |
| error("Filter keyword not allowed for this type", attribute, |
| OSBPTypesPackage.Literals.LATTRIBUTE__RANGE_FILTERING); |
| } else { |
| error("Range keyword not allowed for this type", attribute, |
| OSBPTypesPackage.Literals.LATTRIBUTE__FILTERING); |
| } |
| } |
| } |
| |
| @Check(CheckType.NORMAL) |
| public void checkDuplicatePersistentFQN_InProject(LEntity entity) { |
| LEntityPersistenceInfo info = entity.getPersistenceInfo(); |
| if (info == null) { |
| return; |
| } |
| Map<IContainer, List<LEntityPersistenceInfo>> lTypes = getAllPersistentFQNsFor(info); |
| for (Map.Entry<IContainer, List<LEntityPersistenceInfo>> temp : lTypes.entrySet()) |
| if (temp.getValue().size() > 1) { |
| error(String.format("Persistence type %s is already defined!", |
| qnp.getFullyQualifiedName(info).toString()), entity, |
| OSBPEntityPackage.Literals.LENTITY__PERSISTENCE_INFO, |
| ValidationMessageAcceptor.INSIGNIFICANT_INDEX, CODE__DUPLICATE_PERSISTENCE, (String[]) null); |
| } |
| } |
| |
| private void sendIgnoredInheritancePropertyWarning(LEntityInheritanceStrategy stgy, EAttribute att) { |
| warning("Inherited from parent entity. Will be ignored.", stgy, att, |
| ValidationMessageAcceptor.INSIGNIFICANT_INDEX, CODE__DIFFERING_INHERITANCE_FROM_SUPERTYPE, |
| (String[]) null); |
| } |
| |
| public Map<IContainer, List<LEntityPersistenceInfo>> getAllPersistentFQNsFor(LEntityPersistenceInfo info) { |
| Map<IContainer, List<LEntityPersistenceInfo>> allEntities = new HashMap<IContainer, List<LEntityPersistenceInfo>>(); |
| IResourceDescriptions resourceDescriptions = resourceDescriptionsProvider |
| .getResourceDescriptions(info.eResource()); |
| IResourceDescription resourceDescription = resourceDescriptions |
| .getResourceDescription(info.eResource().getURI()); |
| List<IContainer> visiblecontainers = containermanager.getVisibleContainers(resourceDescription, |
| resourceDescriptions); |
| for (IContainer container : visiblecontainers) { |
| List<LEntityPersistenceInfo> types = new ArrayList<LEntityPersistenceInfo>(); |
| allEntities.put(container, types); |
| for (IEObjectDescription eObjectDescription : container.getExportedObjects( |
| OSBPEntityPackage.Literals.LENTITY_PERSISTENCE_INFO, qnp.getFullyQualifiedName(info), true)) { |
| types.add((LEntityPersistenceInfo) eObjectDescription.getEObjectOrProxy()); |
| } |
| } |
| return allEntities; |
| } |
| |
| @Check |
| public void checkClassPath(LTypedPackage entityModel) { |
| TypeReferences typeReferences = getServices().getTypeReferences(); |
| final JvmGenericType listType = (JvmGenericType) typeReferences.findDeclaredType(List.class, entityModel); |
| if (listType == null || listType.getTypeParameters().isEmpty()) { |
| error("Couldn't find a JDK 1.5 or higher on the project's classpath.", entityModel, |
| OSBPTypesPackage.Literals.LPACKAGE__NAME, CODE__MISSING__JDK_1_5); |
| } |
| if (typeReferences.findDeclaredType(Persistence.class, entityModel) == null) { |
| error("Couldn't find the mandatory library 'javax.persistence' 2.1.0 or higher on the project's classpath.", |
| entityModel, OSBPTypesPackage.Literals.LPACKAGE__NAME, CODE__MISSING__JAVAX_PERSISTENCE); |
| } |
| if (typeReferences.findDeclaredType("org.eclipse.osbp.runtime.common.annotations.Dispose", |
| entityModel) == null) { |
| error("Couldn't find the mandatory library 'org.eclipse.osbp.runtime.common' on the project's classpath.", |
| entityModel, OSBPTypesPackage.Literals.LPACKAGE__NAME, CODE__MISSING__L_RUNTIME_COMMON); |
| } |
| if (typeReferences.findDeclaredType(Extension.class, entityModel) == null) { |
| error("Couldn't find the mandatory library 'org.eclipse.xtext.xbase.lib' 2.11.0 or higher on the project's classpath.", |
| entityModel, OSBPTypesPackage.Literals.LPACKAGE__NAME, CODE__MISSING__XBASE_LIB); |
| } |
| if (typeReferences.findDeclaredType("org.eclipse.osbp.dsl.common.datatypes.IDatatypeConstants", |
| entityModel) == null) { |
| warning("Couldn't find the optional library 'org.eclipse.osbp.dsl.datatype.lib' on the project's classpath. This may cause resolving problems.", |
| entityModel, OSBPTypesPackage.Literals.LPACKAGE__NAME, CODE__MISSING__DATATYPE_LIB); |
| } |
| if (typeReferences.findDeclaredType("javax.validation.Valid", entityModel) == null) { |
| error("Couldn't find the library 'javax.validation' on the project's classpath. This may cause resolving problems.", |
| entityModel, OSBPTypesPackage.Literals.LPACKAGE__NAME, CODE__MISSING__DATATYPE_LIB); |
| } |
| } |
| |
| @Check |
| public void checkDuplicatePersistenceAnnotation(LEntityModel entityModel) { |
| |
| for (JvmGenericType jvmType : EcoreUtil.<JvmGenericType>getObjectsByType(entityModel.eResource().getContents(), |
| TypesPackage.Literals.JVM_GENERIC_TYPE)) { |
| |
| int prePersist = 0; |
| JvmOperation op_prePersist = null; |
| int preUpdate = 0; |
| JvmOperation op_preUpdate = null; |
| int preRemove = 0; |
| JvmOperation op_preRemove = null; |
| int postPersist = 0; |
| JvmOperation op_postPersist = null; |
| int postUpdate = 0; |
| JvmOperation op_postUpdate = null; |
| int postRemove = 0; |
| JvmOperation op_postRemove = null; |
| |
| for (JvmOperation op : jvmType.getDeclaredOperations()) { |
| for (JvmAnnotationReference ann : op.getAnnotations()) { |
| String fqn = ann.getAnnotation().getIdentifier(); |
| if (fqn.equals(PrePersist.class.getCanonicalName())) { |
| prePersist++; |
| op_prePersist = op; |
| } else if (fqn.equals(PreUpdate.class.getCanonicalName())) { |
| preUpdate++; |
| op_preUpdate = op; |
| } else if (fqn.equals(PreRemove.class.getCanonicalName())) { |
| preRemove++; |
| op_preRemove = op; |
| } else if (fqn.equals(PostPersist.class.getCanonicalName())) { |
| postPersist++; |
| op_postPersist = op; |
| } else if (fqn.equals(PostUpdate.class.getCanonicalName())) { |
| postUpdate++; |
| op_postUpdate = op; |
| } else if (fqn.equals(PostRemove.class.getCanonicalName())) { |
| postRemove++; |
| op_postRemove = op; |
| } |
| } |
| } |
| |
| if (prePersist > 1) { |
| error("Only one @PrePersist allowed per entity.", |
| modelAssociations.getPrimarySourceElement(op_prePersist), |
| OSBPTypesPackage.Literals.LANNOTATION_TARGET__ANNOTATIONS, |
| CODE__DUPLICATE__PERSISTENCE_ANNOTATION); |
| } |
| if (preUpdate > 1) { |
| error("Only one @PreUpdate allowed per entity.", |
| modelAssociations.getPrimarySourceElement(op_preUpdate), |
| OSBPTypesPackage.Literals.LANNOTATION_TARGET__ANNOTATIONS, |
| CODE__DUPLICATE__PERSISTENCE_ANNOTATION); |
| } |
| if (preRemove > 1) { |
| error("Only one @PreRemove allowed per entity.", |
| modelAssociations.getPrimarySourceElement(op_preRemove), |
| OSBPTypesPackage.Literals.LANNOTATION_TARGET__ANNOTATIONS, |
| CODE__DUPLICATE__PERSISTENCE_ANNOTATION); |
| } |
| if (postPersist > 1) { |
| error("Only one @PostPersist allowed per entity.", |
| modelAssociations.getPrimarySourceElement(op_postPersist), |
| OSBPTypesPackage.Literals.LANNOTATION_TARGET__ANNOTATIONS, |
| CODE__DUPLICATE__PERSISTENCE_ANNOTATION); |
| } |
| if (postUpdate > 1) { |
| error("Only one @PostUpdate allowed per entity.", |
| modelAssociations.getPrimarySourceElement(op_postUpdate), |
| OSBPTypesPackage.Literals.LANNOTATION_TARGET__ANNOTATIONS, |
| CODE__DUPLICATE__PERSISTENCE_ANNOTATION); |
| } |
| if (postRemove > 1) { |
| error("Only one @PostRemove allowed per entity.", |
| modelAssociations.getPrimarySourceElement(op_postRemove), |
| OSBPTypesPackage.Literals.LANNOTATION_TARGET__ANNOTATIONS, |
| CODE__DUPLICATE__PERSISTENCE_ANNOTATION); |
| } |
| } |
| } |
| |
| @Check(CheckType.NORMAL) |
| public void check_KanbanState(LEntity entity) { |
| |
| int counter = 0; |
| for (LEntityFeature feature : entity.getAllFeatures()) { |
| if (feature instanceof LEntityAttribute) { |
| LEntityAttribute att = (LEntityAttribute) feature; |
| if (att.isAsKanbanState()) { |
| counter++; |
| } |
| } |
| } |
| |
| if (counter > 1) { |
| int i = 0; |
| for (LEntityFeature feature : entity.getFeatures()) { |
| if (feature instanceof LEntityAttribute) { |
| if (((LEntityAttribute) feature).isAsKanbanState()) { |
| error("An entity must only have one kanban state.", |
| OSBPEntityPackage.Literals.LENTITY__FEATURES, i, "", new String[0]); |
| } |
| } |
| i++; |
| } |
| } |
| } |
| |
| @Check(CheckType.NORMAL) |
| public void check_KanbanStateType(LEntityAttribute att) { |
| if (att.isAsKanbanState() && !(att.getType() instanceof LEnum)) { |
| error("Kanban states must be of type Enum", OSBPEntityPackage.Literals.LENTITY_ATTRIBUTE__AS_KANBAN_STATE, |
| "", new String[0]); |
| } |
| } |
| |
| @Check |
| public void check_PersistenceUnitTag(LEntity entity) { |
| if (!entity.isMappedSuperclass()) { |
| if (entity.getPersistenceUnit() == null) { |
| error("An entity must have a persistence unit.", OSBPEntityPackage.Literals.LENTITY__FEATURES); |
| } |
| if (entity.getPersistenceUnit() != null && entity.getPersistenceUnit().trim().isEmpty()) { |
| error("A persistence unit can't be empty.", entity, |
| OSBPEntityPackage.Literals.LENTITY__PERSISTENCE_UNIT); |
| } |
| } |
| } |
| |
| @Check |
| private void check_EntityIdentifiersLength(LEntity entity) { |
| if (entity != null && entity.getPersistenceUnit() != null && !entity.getPersistenceUnit().isEmpty()) { |
| int maxTableNameLength = EnumDatabaseVendor.getMaxTableNameLength(); |
| int maxColumnNameLength = EnumDatabaseVendor.getMaxColumnNameLength(); |
| int maxIndexNameLength = EnumDatabaseVendor.getMaxIndexNameLength(); |
| |
| String expectedFeatureName = PersistenceNamingUtils.camelCaseToUpperCase(entity.getName()); |
| |
| if (entity.getName() != null && !entity.getName().isEmpty() && expectedFeatureName.length() >= maxTableNameLength) { |
| warning("The entity name '" + entity.getName() + "' results to the table name '" + expectedFeatureName |
| + "', which should not exceed " + maxTableNameLength |
| + " characters. Please rename this entity according to the OS.bee naming conventions: http://download.osbee.org/documentation/index.php/Entity_DSL", entity, |
| OSBPEntityPackage.Literals.LENTITY__PERSISTENCE_INFO); |
| } |
| for (LEntityFeature attr : entity.getFeatures()) { |
| if (attr.getName() != null && !attr.getName().isEmpty()) { |
| |
| expectedFeatureName = PersistenceNamingUtils.camelCaseToUpperCase(attr.getName()); |
| |
| if (expectedFeatureName.length() >= maxColumnNameLength && attr instanceof LEntityReference) { |
| warning("The reference name '" + attr.getName() + "' results to the column name '" |
| + expectedFeatureName + "', which should not exceed " + maxColumnNameLength |
| + " characters. Please rename this reference according to the OS.bee naming conventions: http://download.osbee.org/documentation/index.php/Entity_DSL", attr, |
| OSBPEntityPackage.Literals.LENTITY_REFERENCE__TYPE); |
| } else if (expectedFeatureName.length() >= maxColumnNameLength && attr instanceof LEntityAttribute) { |
| warning("The attribute name '" + attr.getName() + "' results to the column name '" |
| + expectedFeatureName + "', which should not exceed " + maxColumnNameLength |
| + " characters. Please rename this attribute according to the OS.bee naming conventions: http://download.osbee.org/documentation/index.php/Entity_DSL", attr, |
| OSBPEntityPackage.Literals.LENTITY_ATTRIBUTE__TYPED_NAME); |
| |
| } else if (attr.getName().length() < maxColumnNameLength && attr instanceof LEntityAttribute) { |
| LScalarType type = ((LEntityAttribute) attr).getType(); |
| |
| if (type instanceof LBean) { |
| for (LBeanFeature feature : ((LBean) type).getFeatures()) { |
| |
| expectedFeatureName = type.getName().toUpperCase() + "_" + feature.getName().toUpperCase(); |
| |
| if (feature.getName() != null && !feature.getName().isEmpty() |
| && expectedFeatureName.length() >= maxColumnNameLength) { |
| |
| warning("The attribute name '" + attr.getName() |
| + "' combined with the bean feature name '" + feature.getName() |
| + "' results to the column name '" + expectedFeatureName |
| + "', which should not exceed " + maxColumnNameLength |
| + " characters. Please rename this attribute according to the OS.bee naming conventions: http://download.osbee.org/documentation/index.php/Entity_DSL", attr, |
| OSBPEntityPackage.Literals.LENTITY_ATTRIBUTE__TYPED_NAME); |
| } |
| } |
| } |
| } |
| } |
| } |
| for (LIndex idx : entity.getIndexes()) { |
| expectedFeatureName = PersistenceNamingUtils.camelCaseToUpperCase(entity.getName() + idx.getName()); |
| if (idx.getName() != null && !idx.getName().isEmpty() |
| && expectedFeatureName.length() >= maxIndexNameLength) { |
| warning("The index id '" + idx.getName() + "' results to the column name '" + expectedFeatureName |
| + "', which should not exceed " + maxIndexNameLength |
| + " characters. Please rename this index according to the OS.bee naming conventions: http://download.osbee.org/documentation/index.php/Entity_DSL", idx, OSBPEntityPackage.Literals.LINDEX__NAME); |
| } |
| } |
| for (LEntitySuperIndex superidx : entity.getSuperIndex()) { |
| // TODO check naming policy after super indexes fully implemented |
| expectedFeatureName = PersistenceNamingUtils |
| .camelCaseToUpperCase(entity.getName() + superidx.getName()); |
| if (superidx.getName() != null && !superidx.getName().isEmpty() |
| && expectedFeatureName.length() >= maxIndexNameLength) { |
| warning("The super index id '" + superidx.getName() + "' results to the column name '" |
| + expectedFeatureName + "', which should not exceed " + maxIndexNameLength |
| + " characters. Please rename this super index according to the OS.bee naming conventions: http://download.osbee.org/documentation/index.php/Entity_DSL", superidx, |
| OSBPEntityPackage.Literals.LENTITY_SUPER_INDEX__NAME); |
| } |
| } |
| } |
| } |
| } |