/**
* Copyright (c) 2009-2011, The HATS Consortium. All rights reserved.
* This file is licensed under the terms of the Modified BSD License.
*/
package abs.frontend.delta;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import abs.frontend.analyser.SemanticError;
import abs.frontend.analyser.ErrorMessage;
import abs.frontend.analyser.SemanticConditionList;
import abs.frontend.analyser.TypeError;
import abs.frontend.ast.*;
public class ProductLineTypeAnalysisHelper {
/*
* An IFΔJ product line is type-safe if the following conditions hold:
* (i) it is strongly unambiguous,
* (ii) its product generation mapping is total, and
* (iii) all its products are well-typed IFJ programs.
*/
public static void typeCheckPL(ProductLine pl, SemanticConditionList errors) {
assert pl != null;
if (wellFormedProductLine(pl, errors)) {
// Check strong unambiguity
checkStrongUnambiguity(pl, errors);
// Build the product family generation trie. Hereby check that
// - product generation mapping is total
// - TODO all products are well-typed programs
DeltaTrie pfgt = buildPFGT(pl, errors);
//System.out.println("Trie:\n" + pfgt.toString());
//System.out.println("Trie height:\n" + pfgt.height());
}
}
/*
* Check that the 'productline' declaration is well formed. This means...
* - after clauses do not reference any deltaIDs that do not have their own delta clause
* - deltas named in the productline correspond to actual DeltaDecls
*/
protected static boolean wellFormedProductLine(ProductLine pl, SemanticConditionList e) {
boolean wellformed = true;
// preliminaries
final Set<String> declaredDeltas = pl.getModel().getDeltaDeclsMap().keySet();
final Set<String> referencedDeltas = new HashSet<String>(pl.getDeltaClauses().getNumChild());
for (DeltaClause clause : pl.getDeltaClauses())
referencedDeltas.add(clause.getDeltaspec().getDeltaID());
// check
for (DeltaClause clause : pl.getDeltaClauses()) {
// ensure deltas in the productline correspond to actual DeltaDecls
if (!declaredDeltas.contains(clause.getDeltaspec().getDeltaID())) {
e.add(new SemanticError(clause, ErrorMessage.NO_DELTA_DECL, clause.getDeltaspec().getDeltaID()));
wellformed = false;
}
// ensure 'after' clauses do not reference any deltaIDs that do not have their own delta clause
for (DeltaID id : clause.getAfterDeltaIDs()) {
String afterID = id.getName();
if (!referencedDeltas.contains(afterID)) {
e.add(new SemanticError(clause, ErrorMessage.MISSING_DELTA_CLAUSE_ERROR, afterID, pl.getName()));
wellformed = false;
}
}
}
return wellformed;
}
public static DeltaTrie buildPFGT(ProductLine pl, SemanticConditionList errors) {
Model model = pl.getModel();
DeltaTrie trie = new DeltaTrie(model, errors);
for (Product product : model.getProductList()) {
// for each product: obtain sequence of deltas & add it to the trie
Set<String> applicableDeltas = pl.findApplicableDeltas(product);
List<String> productGenerationString = pl.sortDeltas(applicableDeltas);
trie.addWord(productGenerationString, product);
}
return trie;
}
public static void checkStrongUnambiguity(ProductLine pl, SemanticConditionList l) {
isStronglyUnambiguous(pl, l);
}
/*
* A product line is strongly unambiguous if each set in the partition of
* the delta modules specified in the product-line declaration is
* consistent, that is, if one delta module in a set adds or removes a
* class, no other delta module in the same set may add, remove or modify
* the same class, and the modifications of the same class in different
* delta modules in the same set have to be disjoint.
*/
public static boolean isStronglyUnambiguous(ProductLine pl, SemanticConditionList l) {
boolean result = true;
Model model = pl.getModel();
// System.out.print("Delta partition: ");
// for (Set<String> set : pl.getDeltaPartition()) {
// System.out.print("{");
// for (String el : set)
// System.out.print(" " + el + " ");
// System.out.print("} ");
// }
// System.out.println();
assert pl.getDeltaPartition() != null;
for (Set<String> set : pl.getDeltaPartition()) {
// Remember the names of classes and methods modified by deltas in
// current set
// { Module.Class -> { Method -> Delta } }
// { Module.Class -> { "CLASS" -> Delta } }
Map<String, Map<String, String>> cache = new HashMap<String, Map<String, String>>();
for (String deltaID : set) {
// assumes the DeltaDecl corresponding to deltaID exists (wellFormedProductLine)
DeltaDecl delta = model.getDeltaDeclsMap().get(deltaID);
assert delta.getModuleModifiers() != null;
for (ModuleModifier moduleModifier : delta.getModuleModifiers()) {
if (moduleModifier instanceof ModifyClassModifier) {
//String methodID;
String prefix = ((ClassModifier) moduleModifier).qualifiedName();
for (Modifier mod : ((ModifyClassModifier) moduleModifier).getModifiers()) {
/*if (mod instanceof AddMethodModifier)
methodID = ((AddMethodModifier) mod).getMethodImpl().getMethodSig().getName();
else if (mod instanceof RemoveMethodModifier)
methodID = ((RemoveMethodModifier) mod).getMethodSig().getName();
else if (mod instanceof ModifyMethodModifier)
methodID = ((ModifyMethodModifier) mod).getMethodImpl().getMethodSig().getName();
else
continue;*/
if(mod instanceof DeltaTraitModifier) {
HashSet<String> methodIDSet = new HashSet<>();
((DeltaTraitModifier) mod).collectMethodIDs(methodIDSet, model);
for (String methodID : methodIDSet) {
result = result & doThings(pl, l, methodID, prefix, deltaID, cache);
}
}
}
} else if (moduleModifier instanceof AddClassModifier
|| moduleModifier instanceof RemoveClassModifier) {
String prefix = ((ClassModifier) moduleModifier).qualifiedName();
if (cache.containsKey(prefix)) {
result = false;
assert ! cache.get(prefix).isEmpty();
String otherDeltaID = cache.get(prefix).values().iterator().next();
l.add(new TypeError(pl, ErrorMessage.AMBIGUOUS_PRODUCTLINE, pl.getName(), deltaID, otherDeltaID, prefix));
} else {
cache.put(prefix, new HashMap<String, String>());
cache.get(prefix).put("CLASS", deltaID);
}
}
}
// TODO apply same reasoning to other elements: fields,
// functions, ADTs, etc
}
}
// FIXME remove boolean result unless needed
return result;
}
private static boolean doThings(ProductLine pl, SemanticConditionList l, String methodID, String prefix, String deltaID, Map<String, Map<String, String>> cache) {
boolean result = true;
if (cache.containsKey(prefix)) {
if (cache.get(prefix).containsKey(methodID)) {
result = false;
l.add(new TypeError(pl, ErrorMessage.AMBIGUOUS_PRODUCTLINE, pl.getName(), deltaID, cache.get(prefix).get(methodID), prefix + ", method " + methodID));
} else if (cache.get(prefix).containsKey("CLASS")) {
result = false;
l.add(new TypeError(pl, ErrorMessage.AMBIGUOUS_PRODUCTLINE, pl.getName(), deltaID, cache.get(prefix).get("CLASS"), prefix));
} else {
cache.get(prefix).put(methodID, deltaID);
}
} else {
cache.put(prefix, new HashMap<String, String>());
cache.get(prefix).put(methodID, deltaID);
}
return result;
}
/*
* Build all SPL configurations (valid feature selections, ignoring attributes), one by one
* The purpose is to measure how long this takes, so we can compare it with the performance of type checking the SPL.
*
*/
public static void buildAllConfigurations(Model m) {
for (Product product : m.getProductList()) {
System.out.println("Flattening product: " + product.getFeatureSetAsString());
Model thisModel = m.treeCopyNoTransform();
thisModel.flattenForProduct(product);
}
}
}