package fr.imag.adele.apam.util;
import java.util.HashSet;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import fr.imag.adele.apam.CST;
import fr.imag.adele.apam.Component;
import fr.imag.adele.apam.Composite;
import fr.imag.adele.apam.CompositeType;
import fr.imag.adele.apam.Implementation;
import fr.imag.adele.apam.Instance;
import fr.imag.adele.apam.Specification;
import fr.imag.adele.apam.impl.ImplementationImpl;
public class Visible {
private static Logger logger = LoggerFactory.getLogger(Visible.class);
/**
* returns true if Component source can establish a wire or link towards
* component target.
*/
public static boolean isVisible(Component source, Component target) {
assert source != null && target != null;
if (source instanceof Specification) {
return true;
}
if (source instanceof Implementation) {
if (target instanceof Specification) {
return isVisible((Implementation) source, (Specification) target);
}
if (target instanceof Implementation) {
return isVisible((Implementation) source, (Implementation) target);
}
if (target instanceof Instance) {
return isVisible((Implementation) source, (Instance) target);
}
}
if (source instanceof Instance) {
if (source instanceof Composite && target instanceof Instance && ((Instance) target).getComposite().equals(source)) {
return false; // Forbid a link between a Composite and a component contained by this composite
//TODO
} else {
return isVisibleIn(((Instance) source).getComposite(), target);
}
}
return false;
}
private static boolean isVisible(Implementation source, Implementation target) {
// They have a composite type in common
Set<CompositeType> intersection = new HashSet<CompositeType>(source.getInCompositeType());
intersection.retainAll(target.getInCompositeType());
if (!intersection.isEmpty()) {
return true;
}
// If the target is visible from at least one of the source composite
// types
for (CompositeType sourceCompositeType : source.getInCompositeType()) {
if (isVisibleIn(sourceCompositeType, target)) {
return true;
}
}
return false;
}
private static boolean isVisible(Implementation source, Instance target) {
// If target in same CT than source
if (source.getInCompositeType().contains(target.getComposite().getCompType())) {
return true;
}
// If the target is visible from at least one of the source composite
// types
for (CompositeType sourceCompositeType : source.getInCompositeType()) {
if (isVisibleIn(sourceCompositeType, target)) {
return true;
}
}
return false;
}
private static boolean isVisible(Implementation source, Specification target) {
return true;
}
/**
* returns true if an instance inside a source composite can establish a
* wire or link towards component target.
*/
public static boolean isVisibleIn(Composite source, Component target) {
assert source != null && target != null;
if (target instanceof Specification) {
return true;
}
if (target instanceof Implementation) {
return isVisibleIn(source.getCompType(), (Implementation) target);
}
if (target instanceof Instance) {
return isVisibleIn(source, (Instance) target);
}
return true;
}
/**
* returns true if an instance is visible from inside a composite.
*/
private static boolean isVisibleIn(Composite source, Instance target) {
assert source != null && target != null && target.getComposite() != null;
/*
* Check if the target is in the source composite
*/
if (target.getComposite().equals(source)) {
return true;
}
// Check if target is visible from the instances of source type
if (isVisibleIn(source.getCompType(), target)) {
return true;
}
// Check if the target is visible to other components in the same
// application
String applicationExports = target.getComposite().getCompType().getCompoDeclaration().getVisibility().getApplicationInstances();
if (matchVisibilityExpression(applicationExports, target, target.getComposite())) {
return target.getAppliComposite().equals(source.getAppliComposite());
}
return false;
}
/**
* returns true if an implementation is visible from inside a composite
* type.
*/
private static boolean isVisibleIn(CompositeType source, Implementation target) {
assert source != null && target != null && !target.getInCompositeType().isEmpty();
if (target.getInCompositeType().contains(source)) {
return true;
}
// First check if target can be imported (borrowed) from source
String imports = source.getCompoDeclaration().getVisibility().getImportImplementations();
if (!matchVisibilityExpression(imports, target, source)) {
logger.warn("Imlplementation is not visible " + target + ". Doesn't match imports from " + source+ " :"+imports);
return false;
}
// Check if at least one composite type that owns target exports it.
for (CompositeType deployingTarget : target.getInCompositeType()) {
String exports = deployingTarget.getCompoDeclaration().getVisibility().getExportImplementations();
if (matchVisibilityExpression(exports, target, deployingTarget)) {
return true;
}
}
logger.warn("Imlplementation is not visible " + target + ". Doesn't match exports from " + target.getInCompositeType());
return false;
}
/**
* returns true if an instance is visible from inside any composite of the
* source type.
*/
private static boolean isVisibleIn(CompositeType source, Instance target) {
assert source != null && target != null && target.getComposite() != null;
// Check if target can be imported from source
String imports = source.getCompoDeclaration().getVisibility().getImportInstances();
if (!matchVisibilityExpression(imports, target, source)) {
return false;
}
// Check if the target is exported by its owning composite
String exports = target.getComposite().getCompType().getCompoDeclaration().getVisibility().getExportInstances();
if (matchVisibilityExpression(exports, target, target.getComposite())) {
return true;
}
return false;
}
/**
* return true if expression is null, "true" or if the component matches the
* expression. Substitution, if any, is with evaluated in the specified
* context.
*
*/
private static boolean matchVisibilityExpression(String expre, Component comp, Component context) {
if ((expre == null) || expre.isEmpty() || expre.equals(CST.V_TRUE)) {
return true;
}
if (expre.equals(CST.V_FALSE)) {
return false;
}
// context is used for the substitution, if any.
ApamFilter f = ApamFilter.newInstanceApam(expre, context);
if (f == null) {
// Bad filter
return false;
}
return comp.match(f);
}
}