package fr.imag.adele.apam.maven.plugin.validation; import java.util.ArrayList; import java.util.List; 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.ResourceReference; import fr.imag.adele.apam.declarations.repository.ComponentIndex; import fr.imag.adele.apam.declarations.repository.Repository; /** * This class keeps a set of components referenced during the validation. * * Unlike a normal component repository, components in the context are effective (all group's features * are automatically inherited) for avoiding navigation up the group hierarchy. * * The set of loaded components is held in a cache, so that we consistently return the same declaration, * specially when the version ranges are not precise. * * @author vega * */ public class ValidationContext implements Repository { /** * The repository chain to lookup for components */ private final Repository repository; /** * The index of loaded original (without inheritance) components */ private final ComponentIndex originalCache; /** * The index of loaded effective (with inheritance) components */ private final ComponentIndex effectiveCache; /** * The list of loaded declarations (both original and effective) * * NOTE this list keeps strong references to the loaded declarations, so that * the cached indexes are never garbage collected */ private List<ComponentDeclaration> loaded; public ValidationContext(Repository repository) { this.repository = repository; this.loaded = new ArrayList<ComponentDeclaration>(); this.originalCache = new ComponentIndex(); this.effectiveCache = new ComponentIndex(); } @Override public <C extends ComponentDeclaration> C getComponent(ComponentReference<C> reference) { return getComponent(reference,false); } @Override public <C extends ComponentDeclaration> C getComponent(VersionedReference<C> reference) { return getComponent(reference,false); } /** * Get the component declaration associated to the specified reference. If there are several versions of * the component, selects an arbitrary one. * * Optionally, it is possible to request an effective declaration, that inherit all features from its group */ public <C extends ComponentDeclaration> C getComponent(ComponentReference<C> reference, boolean effective) { return reference != null ? getComponent(VersionedReference.any(reference),effective) : null; } /** * Get the component declaration associated to the specified reference. If there are several versions of * the component, selects an arbitrary one among the specified range. * * Optionally, it is possible to request an effective declaration, that inherit all features from its group */ public <C extends ComponentDeclaration> C getComponent(VersionedReference<C> reference, boolean effective) { if (reference == null) return null; /* * First check the loaded components */ C cached = effective ? effectiveCache.getComponent(reference) : originalCache.getComponent(reference); if (cached != null) return cached; /* * Otherwise we try to load the component from repository * */ C component = repository.getComponent(reference); if (component == null) return null; /* * update cache */ originalCache.put(component); loaded.add(component); /* * Load the parent group, this may fail if the group or one of its ancestors is not * in the repository */ ComponentDeclaration group = getComponent(component.getGroupVersioned(),true); if (effective && component.getGroup() != null && group == null) return null; /* * Compute the effective declaration. */ @SuppressWarnings("unchecked") C effectiveDeclaration = (C) component.getEffectiveDeclaration(group); if (effective && effectiveDeclaration == null) return null; /* * update cache */ effectiveCache.put(effectiveDeclaration); loaded.add(effectiveDeclaration); return effective ? effectiveDeclaration : component; } /** * Determines if the candidate is an ancestor (or equals) to the component * * TODO This method should be directly available in class {@link ComponentDeclaration}, this requires to migrate * part of the validation and repository API directly to the declarations package. */ public boolean isAncestor(ComponentDeclaration component, ComponentReference<?> candidate, boolean orEquals) { ComponentDeclaration ancestor = orEquals ? component : getComponent(component.getGroupVersioned()); while (ancestor != null) { if (ancestor.getReference().equals(candidate)) return true; ancestor = getComponent(ancestor.getGroupVersioned()); } return false; } /** * Determines if a component is a candidate to satisfy a relation * * TODO This method should be directly available in class {@link RelationDeclaration}, this requires to migrate * part of the validation and repository API directly to the declarations package. */ protected boolean isCandidateTarget(RelationDeclaration relation, ComponentDeclaration candidate) { ComponentReference<?> targetComponent = relation.getTarget().as(ComponentReference.class); ResourceReference targetResource = relation.getTarget().as(ResourceReference.class); if (targetComponent != null) { return isAncestor(candidate,targetComponent,true); } else if (targetResource != null) { return candidate.getProvidedResources(ResourceReference.class).contains(targetResource); } else { return false; } } /** * Reinitializes the context */ public void reset() { originalCache.clear(); effectiveCache.clear(); loaded.clear(); } }