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