package fr.imag.adele.apam.maven.plugin.validation; import java.util.HashSet; import java.util.Set; import fr.imag.adele.apam.CST; import fr.imag.adele.apam.declarations.ComponentDeclaration; import fr.imag.adele.apam.declarations.ComponentKind; import fr.imag.adele.apam.declarations.CompositeDeclaration; import fr.imag.adele.apam.declarations.GrantDeclaration; import fr.imag.adele.apam.declarations.InstanceDeclaration; import fr.imag.adele.apam.declarations.OwnedComponentDeclaration; import fr.imag.adele.apam.declarations.PropertyDefinition; import fr.imag.adele.apam.declarations.RelationDeclaration; import fr.imag.adele.apam.declarations.RelationPromotion; import fr.imag.adele.apam.declarations.SpecificationDeclaration; import fr.imag.adele.apam.declarations.VisibilityDeclaration; import fr.imag.adele.apam.declarations.references.components.ComponentReference; import fr.imag.adele.apam.declarations.references.resources.InterfaceReference; import fr.imag.adele.apam.declarations.references.resources.MessageReference; import fr.imag.adele.apam.declarations.references.resources.ResourceReference; import fr.imag.adele.apam.declarations.repository.maven.Classpath; import fr.imag.adele.apam.maven.plugin.validation.property.EnumerationType; import fr.imag.adele.apam.maven.plugin.validation.property.Type; import fr.imag.adele.apam.maven.plugin.validation.property.TypeParser; import fr.imag.adele.apam.util.ApamFilter; import fr.imag.adele.apam.util.Util; public class CompositeValidator extends ComponentValidator<CompositeDeclaration> { /** * The parser used to validate all defined property types */ private final TypeParser typeParser; /** * The start instance validator */ private final InstanceValidator instanceValidator; /** * The contextual dependencies validator */ private final RelationValidator contextuallRelationValidator; public CompositeValidator(ValidationContext context, Classpath classpath) { super(context, classpath); this.typeParser = new TypeParser(); this.instanceValidator = new InstanceValidator(this); this.contextuallRelationValidator = new RelationValidator(this); } /** * Parses the specified type */ protected Type getType(PropertyDefinition property) { return typeParser.parse(property.getType()); } @Override public Void validate(CompositeDeclaration component) { Void result = super.validate(component); validateMain(); validateContent(); return result; } /** * Validates the main implementation */ private void validateMain() { /* * Abstract composites have no main component, but must not provide any resource */ if (getComposite().getMainComponent() == null) { if (! getComposite().getProvidedResources(ResourceReference.class).isEmpty()) { error(" abstract composites can not provide resources"); } return; } /* * Concrete composites must have a main component that provides everything provided by the composite */ ComponentDeclaration main = getComponent(getComposite().getMainComponent(),true); if (main == null) { error(" main component "+getComposite().getMainComponent().getName()+" could not be found"); return; } if (getComposite().getGroupVersioned() != null && main.getGroupVersioned() != null && !getComposite().getGroupVersioned().equals(main.getGroupVersioned()) ) { error("main component "+ main.getName() + " must implement specification " + getComposite().getGroupVersioned().getName()); } validateMainProvides(main,InterfaceReference.class); validateMainProvides(main,MessageReference.class); } /** * Validates the provided resources of the main implementation */ private void validateMainProvides(ComponentDeclaration main, Class<? extends ResourceReference> kind) { Set<? extends ResourceReference> compositeResources = getComposite().getProvidedResources(kind); Set<? extends ResourceReference> mainResources = main.getProvidedResources(kind); if (!mainResources.containsAll(compositeResources)) { error("invalid main implementation, "+ main.getName() + " must provide " + Util.list(compositeResources,true)); } } /** * check all the characteristics that can be found in the content management declaration * */ private void validateContent() { validateVisibility(); validateOwn(); validatePromotions(); validateStart(); validateContextualRelations(); } private void validateVisibility() { VisibilityDeclaration visibility = getComposite().getVisibility(); validateVisibilityExpression(visibility.getApplicationInstances(), "bad expression in ExportApp visibility"); validateVisibilityExpression(visibility.getExportImplementations(),"bad expression in Export implementation visibility"); validateVisibilityExpression(visibility.getExportInstances(),"bad expression in Export instance visibility"); validateVisibilityExpression(visibility.getImportImplementations(),"bad expression in Imports implementation visibility"); validateVisibilityExpression(visibility.getImportInstances(),"bad expression in Imports instance visibility"); } private void validateVisibilityExpression(String expression, String message) { if (expression == null || expression.equals(CST.V_FALSE) || expression.equals(CST.V_TRUE)) { return; } ApamFilter parsedExpression = parseFilter(expression); if (parsedExpression == null) { error(message+" "+quoted(expression)); } } /** * Validate own declarations */ private void validateOwn() { /* * A state may optionally be defined to allow grant specification */ Type stateType = validateState(); /* * The composite must be a singleton to define owns */ if (!getComposite().getOwnedComponents().isEmpty() && !getComposite().isSingleton()) { error("invalid own expression, a composite must be a singleton to define "+quoted("own")+" clauses"); } /* * Check that a single own clause is defined for a component and its members */ Set<ComponentReference<?>> ownedComponents = new HashSet<ComponentReference<?>>(); for (OwnedComponentDeclaration ownDeclaration : getComposite().getOwnedComponents()) { ComponentDeclaration owned = getComponent(ownDeclaration.getComponent(),true); if (owned == null) { error("invalid own expression, unknown component " + ownDeclaration.getComponent().getName()); continue; } /* * Verify the property used to optionally filter owned components is declared, and is an enumeration * that contains the specified values */ if (ownDeclaration.getProperty() != null) { String propertyName = ownDeclaration.getProperty().getIdentifier(); PropertyDefinition property = owned.getPropertyDefinition(propertyName); if (property == null) { error("invalid own expression, undefined property "+ quoted(propertyName) +" in component "+ owned.getName()); continue; } Type propertyType = getType(property); if (propertyType == null || !(propertyType instanceof EnumerationType)) { error("invalid own expression, property "+ quoted(propertyName) + " of component " + owned.getName() + " is not an enumeration"); continue; } if (ownDeclaration.getValues().isEmpty()) { error("invalid own expression, values not specified for property "+ quoted(propertyName) + " of component " + owned.getName()); continue; } for (String value : ownDeclaration.getValues()) { if (propertyType.value(value) == null) { error("invalid own expression, value "+quoted(value)+" is not valid for property "+ quoted(propertyName) + " of component " + owned.getName()); } } } /* * Check that a single own clause applies for the same component and its members. At execution, it must also * be checked that if there are other grant clauses in other composites for the same component, they must * specify the same property and different values. */ if (ownedComponents.contains(owned.getReference())) { error("invalid own expression, another own clause exists for "+ owned.getName()+ " in this composite declaration"); continue; } ownedComponents.add(owned.getReference()); if (owned.getGroup() != null) { ownedComponents.add(owned.getGroup()); } validateGrant(owned,ownDeclaration,stateType); } } private void validateGrant(ComponentDeclaration owned, OwnedComponentDeclaration ownDeclaration, Type stateType) { /* * Verify a state has been defined */ if (stateType == null && !ownDeclaration.getGrants().isEmpty()) { error("invalid grant expression, state is not defined in composite "); return; } Set<String> grantedStates = new HashSet<String>(); for (GrantDeclaration grantDeclaration : ownDeclaration.getGrants()) { // Check that grant state values are valid for (String grantState : grantDeclaration.getStates()) { if (stateType.value(grantState) == null) { error("invalid grant expression, value "+quoted(grantState)+" is not valid for state property "+ quoted(getComposite().getStateProperty().getIdentifier())); } if (grantedStates.contains(grantState)) { error("invalid grant expression, state value"+quoted(grantState)+" alreday speciifed in another gran clause "+grantDeclaration); } grantedStates.add(grantState); } // Check that the granted component exists ComponentDeclaration granted = getComponent(grantDeclaration.getRelation().getDeclaringComponent(),true); if (granted == null) { error("invalid grant expression, unknown component "+grantDeclaration.getRelation().getDeclaringComponent().getName()+ " in grant expression "+grantDeclaration); continue; } // Check that the component is a singleton if (granted.isDefinedSingleton() && ! granted.isSingleton()) { warning("invalid grant expression, component "+grantDeclaration.getRelation().getDeclaringComponent().getName()+ " is not a singleton "+grantDeclaration); } // Check that the relation exists and has as target the OWN resource // OWN is a specification or an implem but the granted relation can be anything RelationDeclaration grantedRelation = granted.getRelation(grantDeclaration.getRelation().getIdentifier()); if (grantedRelation == null) { error("invalid grant expression, the relation "+ quoted(grantDeclaration.getRelation().getIdentifier()) +" is not defined in component " + granted.getName()); } else if (!isCandidateTarget(grantedRelation,owned)) { error("invalid grant expression, the relation "+ quoted(grantDeclaration.getRelation().getIdentifier()) +" does not refer to the owned component " + owned.getName()); } } } /** * Validate the specified state property declaration * */ private EnumerationType validateState() { PropertyDefinition.Reference state = getComposite().getStateProperty(); if (state == null) { return null; } ComponentDeclaration declaring = getComponent(state.getDeclaringComponent(),true); if (declaring == null) { error("invalid state property "+quoted(state.getIdentifier())+", declaring component "+state.getDeclaringComponent().getName()+" is unavailable"); return null; } if (! declaring.getKind().equals(ComponentKind.IMPLEMENTATION)) { error("invalid state property "+quoted(state.getIdentifier())+", declaring component "+state.getDeclaringComponent().getName()+" is not an implementation"); return null; } // Attribute state must be defined on the implementation. PropertyDefinition property = declaring.getPropertyDefinition(state.getIdentifier()); if (property == null) { error("invalid state property "+quoted(state.getIdentifier())+", not declared in component "+state.getDeclaringComponent().getName()); return null; } Type type = getType(property); if (!(type instanceof EnumerationType)) { error("invalid state property "+quoted(state.getIdentifier())+", must be an enumeration and it is actually of type "+type); return null; } return (EnumerationType) type; } /** * Cannot check if the component relation is valid. Only checks that the composite relation is declared, * and that the component is known. * * @param composite */ private void validatePromotions() { for (RelationPromotion promotion : getComposite().getPromotions()) { ComponentDeclaration source = getComponent(promotion.getContentRelation().getDeclaringComponent(),true); if (source == null) { error("invalid promotion, the source component "+ promotion.getContentRelation().getDeclaringComponent() +" is unknown"); } RelationDeclaration promotedRelation = source != null ? source.getRelation(promotion.getContentRelation().getIdentifier()) : null; // Check if the dependencies are compatible if (source != null && promotedRelation == null) { error("invalid promotion, the promoted relation "+ quoted(promotion.getContentRelation().getIdentifier()) +" is not defined in component "+source.getName()); continue; } RelationDeclaration compositeRelation = getComposite().getRelation(promotion.getCompositeRelation()); if (compositeRelation == null) { error("invalid promotion, the composite relation "+ quoted(promotion.getCompositeRelation().getIdentifier()) +" is not defined"); continue; } // Both the composite and the component have a relation with the right id. Check if the targets are compatible if (promotedRelation != null && compositeRelation != null && !matchPromotion(promotedRelation, compositeRelation)) { error("invalid promotion, the promoted relation "+ quoted(promotion.getContentRelation().getIdentifier()) + " does not match the composite relation " + quoted(promotion.getCompositeRelation().getIdentifier())); } } } // Copy paste of the Util class ! too bad, this one uses ApamCapability private boolean matchPromotion(RelationDeclaration promotedRelation, RelationDeclaration compositeRelation) { boolean match = false; ComponentDeclaration compositeTarget = getComponent(compositeRelation.getTarget().as(ComponentReference.class),true); if (compositeTarget != null) { /* * If the target of the composite relation is a component, it must satisfy the promoted relation */ match = isCandidateTarget(promotedRelation, compositeTarget); } else { /* * Otherwise, the target resource must match exactly */ match = promotedRelation.getTarget().as(ResourceReference.class) != null && promotedRelation.getTarget().equals(compositeRelation.getTarget()); } return match && ( !promotedRelation.isMultiple() || compositeRelation.isMultiple()); } private void validateStart() { for (InstanceDeclaration instance : getComposite().getInstanceDeclarations()) { validate(instance,instanceValidator); } } private void validateContextualRelations() { for (RelationDeclaration contextual : getComposite().getContextualDependencies()) { validate(contextual,contextuallRelationValidator); } for (RelationDeclaration override : getComposite().getOverridenDependencies()) { validate(override,contextuallRelationValidator); } } protected CompositeDeclaration getComposite() { return super.getComponent(); } @Override protected SpecificationDeclaration getGroup() { return (SpecificationDeclaration) super.getGroup(); } }