package fr.imag.adele.apam.maven.plugin.validation; import fr.imag.adele.apam.declarations.ComponentDeclaration; import fr.imag.adele.apam.declarations.RelationDeclaration; import fr.imag.adele.apam.declarations.references.components.ComponentReference; import fr.imag.adele.apam.declarations.references.components.VersionedReference; import fr.imag.adele.apam.declarations.references.resources.PackageReference; import fr.imag.adele.apam.declarations.references.resources.ResourceReference; import fr.imag.adele.apam.declarations.references.resources.UnknownReference; import fr.imag.adele.apam.declarations.repository.maven.Classpath; import fr.imag.adele.apam.declarations.tools.Reporter; import fr.imag.adele.apam.declarations.tools.Reporter.Severity; import fr.imag.adele.apam.util.ApamFilter; /** * This is the base class of all component validators. * * NOTE Notice that this validator is not thread-safe, as it maintain a state for the current validating * component, it can not be used concurrently. However, several instances of a validator can be used in * parallel, and the same instance can be reused again (after {@link #reset() resetting} it) * * Notice also that all validation are performed in a context composed of a repository of component * declarations an a classpath of available Java classes. * * @author vega * */ public abstract class AbstractValidator<D,R> { /** * The validation classpath to look for referenced Java classes */ private final Classpath classpath; /** * The validation context to look for referenced components */ private final ValidationContext context; /** * Creates a new validator */ protected AbstractValidator(ValidationContext context, Classpath classpath) { this.classpath = classpath; this.context = context; } /** * Create a new children validator. * * Parent validators can delegate validations to its children in order to validate parts of the * component declaration */ protected AbstractValidator(AbstractValidator<?,?> parent) { this(parent.context,parent.classpath); parent.transferStateTo(this); } /** * Get a component from the current validation context */ protected ComponentDeclaration getComponent(ComponentReference<?> reference, boolean effective) { return context.getComponent(reference, effective); } /** * Get a version of a component from the current validation context */ protected ComponentDeclaration getComponent(VersionedReference<?> reference, boolean effective) { return context.getComponent(reference, effective); } /** * Determines if the candidate is an ancestor (or equals) to the component */ protected boolean isAncestor(ComponentDeclaration component, ComponentReference<?> candidate, boolean orEquals) { return context.isAncestor(component, candidate, orEquals); } /** * Determines if a component is a candidate to satisfy a relation */ protected boolean isCandidateTarget(RelationDeclaration relation, ComponentDeclaration candidate) { return context.isCandidateTarget(relation, candidate); } /** * Checks if a resource exists among the bundles in the context classpath * */ protected boolean checkResourceExists(ResourceReference resource) { /* * Can be unknown if the resource is defined by the type of a generic collection, since it is not * possible to get the type at compile time */ if (resource == null || resource instanceof UnknownReference) { return true; } /* * TODO check that the package is exported by some bundle in the classpath */ if (resource.as(PackageReference.class) != null) { return true; } if (! classpath.contains(resource.getJavaType())) { error("the referenced class " + resource.getJavaType() + " does not exist in your build dependencies"); return false; } return true; } /** * Get a parsed filter */ protected ApamFilter parseFilter(String filter) { return ApamFilter.newInstance(filter); } /** * Validates the given declaration, this optionally may return a result */ public abstract R validate(D declaration); /** * Delegates the validation of a nested declaration to another validator, using the * current state this validator. * * NOTE notice that a delegate can be reused to validate several nested declarations, * as long as the validations are not performed concurrently. */ public final <N,S> S validate(N nestedDeclaration, AbstractValidator<N,S> delegate) { S result = null; try { this.transferStateTo(delegate); result = delegate.validate(nestedDeclaration); } finally { delegate.resetState(); } return result; } /** * The reporter to signal errors */ private Reporter reporter; /** * The component currently being validated */ private ComponentDeclaration component; /** * The effective declaration of the group of the component currently being validated */ private ComponentDeclaration group; /** * Initializes the state for validating a new component declaration */ protected void startValidation(ComponentDeclaration component, Reporter reporter) { this.reporter = reporter; this.component = component; this.group = context.getComponent(component.getGroupVersioned(), true); } /** * Initializes the state for validating a new component declaration in the current * context. This is useful to allow validating nested declarations * * TODO We should handle a stack of currently validating components, and give access * to the stack components to allow context sensitive validation */ protected void startValidation(ComponentDeclaration component) { if (this.component != null && !this.component.equals(component)) { this.component = component; this.group = context.getComponent(component.getGroupVersioned(), true); } } /** * Initializes the validation state of a delegate from the current state */ protected void transferStateTo(AbstractValidator<?,?> delegate) { delegate.reporter = this.reporter; delegate.component = this.component; delegate.group = this.group; } /** * Release all references to the current validation state */ public void resetState() { this.reporter = null; this.component = null; this.group = null; } /** * The component currently validated */ protected ComponentDeclaration getComponent() { return component; } /** * The group of the component currently validated, if defined and valid */ protected ComponentDeclaration getGroup() { return group; } /** * Signal an error to the current reporter */ public final void error(String msg) { reporter.report(Severity.ERROR,"Error in component "+ quoted(component.getName()) + " : "+ msg); } /** * Signal a warning to the current reporter */ public final void warning(String msg) { reporter.report(Severity.WARNING,msg); } /** * Signal an information message to the current reporter */ public final void info(String msg) { reporter.report(Severity.INFO,msg); } /** * Utility methods for messages */ protected final String quoted(String value) { StringBuilder quoted = new StringBuilder(); quoted.append("\'").append(value).append("\'"); return quoted.toString(); } }