/**
* Copyright (c) 2009-2011, The HATS Consortium. All rights reserved.
* This file is licensed under the terms of the Modified BSD License.
*/
package abs.backend.prolog;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import abs.frontend.ast.ASTNode;
import abs.frontend.ast.ClassDecl;
import abs.frontend.ast.FunctionDecl;
import abs.frontend.ast.InitBlock;
import abs.frontend.ast.InterfaceDecl;
import abs.frontend.ast.InterfaceTypeUse;
import abs.frontend.ast.MainBlock;
import abs.frontend.ast.MethodImpl;
import abs.frontend.ast.MethodSig;
import abs.frontend.ast.NewExp;
import abs.frontend.ast.ParametricFunctionDecl;
import abs.frontend.typechecker.InterfaceType;
/**
* Auxiliary class for obtaining the reachable methods and functions of a given
* model from a given set of initial entries.
* It can record the processed elements and the reachable elements. It can
* also be queried about the reachability of an element.
*
* @author aeflores
*
*/
public class ReachabilityInformation {
/**
* Records the method names that are reachable. The names can be:
* -Interface name+ method name
* -Class name+ method name
*/
private HashSet<String> reachableMethods;
/**
* Records the class or interface names that are reachable
*/
private HashSet<String> reachableInterfacesOrClasses;
/**
* Records the complete function nodes that are reachable and whether
* they have already been processed or not.
*/
private Hashtable<ASTNode<?>, Boolean> reachableFuncts;
/**
* Records the MainBlocks that are reachable.
* (these will only be added by the constructor)
*/
private Hashtable<MainBlock, Boolean> reachableMainBlocks;
/**
* Records the method names that have been processed. The names can be:
* -Interface name+ method name
* -Class name+ method name
*/
private HashSet<String> processedMethods;
/**
* Flag that indicates if there has been any changes in the reachable sets.
*/
private boolean changed;
/**
* Initializes the databases with an initial set of elements.
* @param entries Initial set of reachable entries.
*/
public ReachabilityInformation(ArrayList<ASTNode<?>> entries) {
// Sets creation
reachableMethods = new HashSet<String>();
reachableInterfacesOrClasses = new HashSet<String>();
reachableFuncts = new Hashtable<ASTNode<?>, Boolean>();
reachableMainBlocks = new Hashtable<MainBlock, Boolean>();
processedMethods = new HashSet<String>();
ClassDecl ownerClass;
abs.frontend.ast.List<InterfaceTypeUse> interfaces;
// Initialization of tables
for (ASTNode<?> entry : entries) {
if (entry instanceof MethodImpl) {
ownerClass = obtainOwnerClass((MethodImpl) entry);
if (ownerClass != null) {
reachableInterfacesOrClasses.add(getClassId(ownerClass));
reachableMethods.add(getMethodId(ownerClass, ((MethodImpl) entry).getMethodSig()));
interfaces = ownerClass.getImplementedInterfaceUseList();
for (InterfaceTypeUse inter : interfaces) {
reachableInterfacesOrClasses.add(getInterfaceId(inter));
reachableMethods.add(getMethodId(inter, ((MethodImpl) entry).getMethodSig()));
}
}
} else if (entry instanceof MainBlock) {
reachableMainBlocks.put((MainBlock) entry, Boolean.FALSE);
} else
reachableFuncts.put(entry, Boolean.FALSE);
}
changed = true;
}
/*
* Methods that create standard name from the different combinations of elements
*/
private String getInterfaceId(InterfaceTypeUse inter) {
return inter.getType().getQualifiedName();
}
private String getMethodId(InterfaceTypeUse inter, MethodSig method) {
return getInterfaceId(inter) + method.getName();
}
private String getInterfaceId(InterfaceType inter) {
return inter.getQualifiedName();
}
private String getMethodId(InterfaceType inter, MethodSig method) {
return getInterfaceId(inter) + method.getName();
}
private String getClassId(ClassDecl clazz) {
return clazz.qualifiedName();
}
private String getMethodId(ClassDecl clazz, MethodSig method) {
return getClassId(clazz) + method.getName();
}
/**
* finds the class where the given class belongs to
* @param node
* @return the class that owns the parameter node or null if node does not belong to any class
*/
private ClassDecl obtainOwnerClass(ASTNode<?> node) {
ASTNode<?> ancestor;
boolean foundClass = false;
ancestor = node;
while (!foundClass) {
ancestor = ancestor.getParent();
if (ancestor != null) {
foundClass = ancestor instanceof ClassDecl;
} else
foundClass = true;
}
// FIXME: these two should be equivalent, so better use
assert ancestor == node.calcContextNode(ClassDecl.class);
return (ClassDecl) ancestor;
}
/**
* returns true if there has been any change since the last time it was executed
* @return
*/
public boolean changed() {
if (changed) {
changed = false;
return true;
} else
return false;
}
/**
* checks if the parameter is reachable
* @param funct
* @return true if funct is reachable, false otherwise
*/
public boolean isReachable(FunctionDecl funct) {
return reachableFuncts.containsKey(funct);
}
/**
* checks if the parameter is reachable
* @param funct
* @return true if funct is reachable, false otherwise
*/
public boolean isReachable(ParametricFunctionDecl funct) {
return reachableFuncts.containsKey(funct);
}
/**
* checks if the parameter is reachable
* @param mBlock
* @return true if mBlock is reachable, false otherwise
*/
public boolean isReachable(MainBlock mBlock) {
return reachableMainBlocks.containsKey(mBlock);
}
/**
* checks if the class is reachable. It looks at the class name
* and all the interface names that are directly or indirectly
* implemented by clazz
* @param clazz
* @return true if clazz is reachable, false otherwise
*/
public boolean isReachable(ClassDecl clazz) {
boolean reachable = false;
abs.frontend.ast.List<InterfaceTypeUse> interfaces = clazz.getImplementedInterfaceUseList();
Iterator<InterfaceTypeUse> it = interfaces.iterator();
//checks if any interface implemented by clazz is reachable
reachable = reachableInterfacesOrClasses.contains(getClassId(clazz));
while (!reachable && it.hasNext()) {
reachable = isReachable(it.next());
}
return reachable;
}
private boolean isReachable(InterfaceTypeUse inter) {
boolean reachable = false;
//checks if inter is reachable
reachable = reachableInterfacesOrClasses.contains(getInterfaceId(inter));
if (!reachable) {
//checks if any interface implemented by inter is reachable
abs.frontend.ast.List<InterfaceTypeUse> interfaces = ((InterfaceDecl) inter.getDecl())
.getExtendedInterfaceUseList();
Iterator<InterfaceTypeUse> it = interfaces.iterator();
while (!reachable && it.hasNext()) {
reachable = isReachable(it.next());
}
}
return reachable;
}
/**
* checks if the method is reachable. It looks at the class name
* and all the interface names that are directly or indirectly
* implemented by the method's class
* @param method
* @return true if method is reachable, false otherwise
*/
public boolean isReachable(MethodImpl method) {
ClassDecl clazz = obtainOwnerClass(method);
boolean reachable = false;
if (clazz != null) {
abs.frontend.ast.List<InterfaceTypeUse> interfaces = clazz.getImplementedInterfaceUseList();
Iterator<InterfaceTypeUse> it = interfaces.iterator();
//checks if the method is reachable with its class name
reachable = reachableMethods.contains(getMethodId(clazz, method.getMethodSig()));
//checks if the method is reachable with any interface name that is
//implemented by its class
while (!reachable && it.hasNext())
reachable = isReachable(it.next(), method.getMethodSig());
return reachable;
} else
return false;
}
private boolean isReachable(InterfaceTypeUse inter, MethodSig method) {
boolean reachable = false;
//checks if the method is reachable with inter
reachable = reachableMethods.contains(getMethodId(inter, method));
if (!reachable) {
//checks if the method is reachable with any interface that is
//implemented by inter
abs.frontend.ast.List<InterfaceTypeUse> interfaces = ((InterfaceDecl) inter.getDecl())
.getExtendedInterfaceUseList();
Iterator<InterfaceTypeUse> it = interfaces.iterator();
while (!reachable && it.hasNext()) {
reachable = isReachable(it.next(), method);
}
}
return reachable;
}
/**
* checks if the constructor is reachable, it is treated as a special
* method called init.
* @param block
* @return true if block is reachable, false otherwise
*/
public boolean isReachable(InitBlock block) {
ClassDecl clazz = obtainOwnerClass(block);
if (clazz != null)
return reachableMethods.contains(getClassId(clazz) + "init");
else
return false;
}
/**
* Sets the parameter to processed
* @param funct
* @return true if the parameter had not been processed before.
*/
public boolean setProcessed(FunctionDecl funct) {
return !reachableFuncts.put(funct, Boolean.TRUE);
}
/**
* Sets the parameter to processed
* @param funct
* @return true if the parameter had not been processed before.
*/
public boolean setProcessed(ParametricFunctionDecl funct) {
return !reachableFuncts.put(funct, Boolean.TRUE);
}
/**
* Sets the parameter to processed
* @param mBlock
* @return true if the parameter had not been processed before.
*/
public boolean setProcessed(MainBlock mBlock) {
return !reachableMainBlocks.put(mBlock, Boolean.TRUE);
}
/**
* Sets the parameter to processed
* @param method
* @return true if the parameter had not been processed before.
*/
public boolean setProcessed(MethodImpl method) {
ClassDecl clazz = obtainOwnerClass(method);
if (clazz != null)
return processedMethods.add(clazz.qualifiedName() + method.getMethodSig().toString());
else
return false;
}
/**
* Sets the parameter to processed
* @param block
* @return true if the parameter had not been processed before.
*/
public boolean setProcessed(InitBlock block) {
ClassDecl clazz = obtainOwnerClass(block);
if (clazz != null)
return processedMethods.add(clazz.qualifiedName() + "init");
else
return false;
}
/**
*Sets the parameter to reachable and not processed if it was not reachable before
* @param funct
* @return true if the parameter was not reachable before
*/
public boolean addReachability(FunctionDecl funct) {
if (!reachableFuncts.containsKey(funct)) {
reachableFuncts.put(funct, Boolean.FALSE);
changed = true;
return true;
}
return false;
}
/**
*Sets the parameter to reachable and not processed if it was not reachable before
* @param funct
* @return true if the parameter was not reachable before
*/
public boolean addReachability(ParametricFunctionDecl funct) {
if (!reachableFuncts.containsKey(funct)) {
reachableFuncts.put(funct, Boolean.FALSE);
changed = true;
return true;
}
return false;
}
/**
*Sets the parameter to reachable and not processed if it was not reachable before
* @param inter
* @return true if the parameter was not reachable before
*/
public boolean addReachability(InterfaceType inter) {
if (reachableInterfacesOrClasses.add(getInterfaceId(inter))) {
changed = true;
return true;
}
return false;
}
/**
*Sets the parameter to reachable and not processed if it was not reachable before
* @param clazz
* @return true if the parameter was not reachable before
*/
public boolean addReachability(ClassDecl clazz) {
if (reachableInterfacesOrClasses.add(getClassId(clazz))) {
changed = true;
return true;
}
return false;
}
/**
*Sets the method to reachable using the interface inter.
* @param inter
* @param method
* @return true if the parameter was not reachable before
*/
public boolean addReachability(InterfaceType inter, MethodSig method) {
if (reachableMethods.add(getMethodId(inter, method))) {
changed = true;
return true;
}
return false;
}
/**
*Sets the method to reachable using the class clazz.
* @param clazz
* @param method
* @return true if the parameter was not reachable before
*/
public boolean addReachability(ClassDecl clazz, MethodSig method) {
if (reachableMethods.add(getMethodId(clazz, method))) {
changed = true;
return true;
}
return false;
}
/**
*Sets the corresponding constructor to reachable.
* @param clazz
* @param newExp only uses to distinguish the method form others
* @return true if the parameter was not reachable before
*/
public boolean addReachability(ClassDecl clazz, NewExp newExp) {
if (reachableMethods.add(getClassId(clazz) + "init")) {
changed = true;
return true;
}
return false;
}
public String toString() {
StringBuilder strBld = new StringBuilder();
strBld.append("Reachable Methods:" + reachableMethods.size() + "\n");
strBld.append(reachableMethods.toString() + "\n");
strBld.append("Reachable Function:" + reachableFuncts.size() + "\n");
strBld.append(reachableFuncts.toString());
return strBld.toString();
}
}