{- |
Module : ./QVTR/StatAna.hs
Description : Static QVTR analysis
Copyright : (c) Daniel Calegari Universidad de la Republica, Uruguay 2013
License : GPLv2 or higher, see LICENSE.txt
Maintainer : dcalegar@fing.edu.uy
Stability : provisional
Portability : portable
-}
module QVTR.StatAna where
import QVTR.As
import QVTR.Sign
import qualified CSMOF.As as CSMOFAs
import qualified CSMOF.Sign as CSMOFSign
import qualified CSMOF.StatAna as CSMOFStatAna
import Common.Result
import Common.GlobalAnnotations
import Common.ExtSign
import Common.AS_Annotation
import qualified Data.Map as Map
import qualified Data.Set as Set
import qualified Common.Lib.Rel as Rel
basicAna :: (Transformation, Sign, GlobalAnnos) ->
Result (Transformation, ExtSign Sign (), [Named Sen])
basicAna (trans, _, _) =
let
(sign, diagSign) = buildSignature trans
(sen, diagSen) = buildSentences sign trans
in Result (reverse diagSign ++ reverse diagSen)
$ Just (trans, mkExtSign sign, sen)
buildSignature :: Transformation -> (Sign, [Diagnosis])
buildSignature (Transformation _ souMet tarMet kS rels) =
let
souMetSign = CSMOFStatAna.buildSignature (third souMet)
tarMetSign = CSMOFStatAna.buildSignature (third tarMet)
(relat, diagn) = buildRelations souMetSign tarMetSign rels
(keyD, diagn2) = buildKeyDefs souMetSign tarMetSign kS
in
(Sign { sourceSign = souMetSign
, targetSign = tarMetSign
, nonTopRelations = fst relat
, topRelations = snd relat
, keyDefs = keyD
}
, diagn ++ diagn2)
buildRelations :: CSMOFSign.Sign -> CSMOFSign.Sign -> [Relation] ->
((Map.Map String RuleDef, Map.Map String RuleDef), [Diagnosis])
buildRelations souMetSign tarMetSign rels =
let
(nonTopRel, topRel) = separateTopFromNonTop rels
calledTopRules = map createCalledTopRule topRel
(nonTopRuleDef, diagn1) = foldr (createRuleDef souMetSign tarMetSign)
(Map.empty, []) (nonTopRel ++ calledTopRules)
(topRuleDef, diagn2) = foldr (createRuleDef souMetSign tarMetSign)
(Map.empty, []) topRel
in
((nonTopRuleDef, topRuleDef), diagn1 ++ diagn2)
separateTopFromNonTop :: [Relation] -> ([Relation], [Relation])
separateTopFromNonTop rels =
case rels of
[] -> ([], [])
r : rest -> let result = separateTopFromNonTop rest
in
if isTop r then
(fst result, r : snd result)
else (r : fst result, snd result)
isTop :: Relation -> Bool
isTop (Relation tp _ _ _ _ _ _ _) = tp
createRuleDef :: CSMOFSign.Sign -> CSMOFSign.Sign -> Relation ->
(Map.Map String RuleDef, [Diagnosis]) ->
(Map.Map String RuleDef, [Diagnosis])
createRuleDef souMetSign tarMetSign (Relation tp rName _ prD souD tarD _ _)
(mapRD, diag) =
let (varTyp, diag2) = getTypesFromVars souMetSign tarMetSign prD souD tarD
in
if tp
then case Map.lookup ("Top_" ++ rName) mapRD of
Nothing -> (Map.insert ("Top_" ++ rName)
(RuleDef ("Top_" ++ rName) tp []) mapRD, diag ++ diag2)
Just r -> (mapRD, mkDiag Error "rule names must be unique"
(QVTR.Sign.name r) : (diag ++ diag2))
else case Map.lookup rName mapRD of
Nothing -> (Map.insert rName (RuleDef rName tp varTyp) mapRD,
diag ++ diag2)
Just r -> (mapRD, mkDiag Error "rule names must be unique"
(QVTR.Sign.name r) : (diag ++ diag2))
-- Generate rule parameters from primitive domains, source and target object domains
getTypesFromVars :: CSMOFSign.Sign -> CSMOFSign.Sign -> [PrimitiveDomain] ->
Domain -> Domain -> ([CSMOFSign.TypeClass], [Diagnosis])
getTypesFromVars souMetSign tarMetSign primD souD tarD =
let
(souDomObj, d1) = getDomainType souMetSign souD
(tarDomObj, d2) = getDomainType tarMetSign tarD
(pTypes, d3) = unzip $ map (getPrimitiveDomainType souMetSign tarMetSign) primD
primTypes = getSomething pTypes
in
case souDomObj of
Nothing -> case tarDomObj of
Nothing -> (primTypes, d1 ++ d2 ++ concat d3)
Just tDO -> (tDO : primTypes, concat d3)
Just sDO -> case tarDomObj of
Nothing -> (sDO : primTypes, d1 ++ d2 ++ concat d3)
Just tDO -> (sDO : (tDO : primTypes), concat d3)
getDomainType :: CSMOFSign.Sign -> Domain -> (Maybe CSMOFSign.TypeClass, [Diagnosis])
getDomainType metSign (Domain _ (ObjectTemplate _ _ dType _)) =
getType (Set.toList (CSMOFSign.types metSign)) dType
getPrimitiveDomainType :: CSMOFSign.Sign -> CSMOFSign.Sign -> PrimitiveDomain ->
(Maybe CSMOFSign.TypeClass, [Diagnosis])
getPrimitiveDomainType souMetSign tarMetSign (PrimitiveDomain _ prType) =
let (typ, diag) = getType (Set.toList (CSMOFSign.types souMetSign)) prType
in
case typ of
Nothing -> let (typ2, diag2) =
getType (Set.toList (CSMOFSign.types tarMetSign)) prType
in
case typ2 of
Nothing -> (typ2, diag ++ diag2)
Just _ -> (typ2, [])
Just _ -> (typ, [])
getType :: [CSMOFSign.TypeClass] -> String -> (Maybe CSMOFSign.TypeClass, [Diagnosis])
getType types dType =
case types of
[] -> (Nothing, [mkDiag Error "type not found" dType])
typ : rest -> if CSMOFSign.name typ == dType then (Just typ, [])
else getType rest dType
getSomething :: [Maybe a] -> [a]
getSomething list =
case list of
[] -> []
el : rest -> case el of
Nothing -> getSomething rest
Just typ -> typ : getSomething rest
-- Creates a non-top version of a top rule in order to generate a parametrized version of itself
createCalledTopRule :: Relation -> Relation
createCalledTopRule (Relation tp a b c d e f g) = Relation (not tp) a b c d e f g
buildKeyDefs :: CSMOFSign.Sign -> CSMOFSign.Sign -> [Key] -> ([(String, String)], [Diagnosis])
buildKeyDefs _ _ [] = ([], [])
buildKeyDefs souMet tarMet (k : rest) =
let (restK, diag) = buildKeyDefs souMet tarMet rest
(ke, diag2) = buildKeyDef souMet tarMet k
in
case ke of
Nothing -> (restK, diag ++ diag2)
Just el -> (el : restK, diag ++ diag2)
buildKeyDef :: CSMOFSign.Sign -> CSMOFSign.Sign -> Key ->
(Maybe (String, String), [Diagnosis])
buildKeyDef souMet tarMet k =
let (typ, diag) = getType (Set.toList (CSMOFSign.types souMet)) (typeName k)
in
case typ of
Nothing -> let (typ2, diag2) = getType (Set.toList (CSMOFSign.types tarMet))
(typeName k)
in
case typ2 of
Nothing ->
(Nothing, mkDiag Error "type not found" (typeName k) :
(diag ++ diag2))
Just _ -> if propKeysCheckOK tarMet (typeName k)
(properties k)
then (Just (metamodel k, typeName k), [])
else (Nothing, mkDiag Error "property not found"
(properties k) : (diag ++ diag2))
Just _ -> if propKeysCheckOK souMet (typeName k) (properties k) then
(Just (metamodel k, typeName k), [])
else (Nothing, mkDiag Error "property not found"
(properties k) : diag)
-- ------ Sentences --------
buildSentences :: Sign -> Transformation -> ([Named Sen], [Diagnosis])
buildSentences sign (Transformation _ souMet tarMet kes rels) =
let
(_, sMetN, _) = souMet
(_, tMetN, _) = tarMet
(keyConstr, diag) = buildKeyConstr sign sMetN tMetN kes
(qvtRules, diag2) = buildRules sign souMet tarMet rels
in
(keyConstr ++ qvtRules, diag ++ diag2)
buildKeyConstr :: Sign -> String -> String -> [Key] -> ([Named Sen], [Diagnosis])
buildKeyConstr _ _ _ [] = ([], [])
buildKeyConstr sign sMetN tMetN (k : rest) =
let
(restK, diag) = buildKeyConstr sign sMetN tMetN rest
(ke, diag2) = buildKeyC sign sMetN tMetN k
in
case ke of
Nothing -> (restK, diag ++ diag2)
Just el -> (el : restK, diag ++ diag2)
buildKeyC :: Sign -> String -> String -> Key -> (Maybe (Named Sen), [Diagnosis])
buildKeyC sign sMetN tMetN k =
if sMetN == metamodel k || tMetN == metamodel k then
let (typ, diag) = getType
(Set.toList (CSMOFSign.types (sourceSign sign))) (typeName k)
in
case typ of
Nothing -> let (typ2, diag2) = getType (Set.toList (CSMOFSign.types
(targetSign sign))) (typeName k)
in
case typ2 of
Nothing -> (Nothing, mkDiag Error "type not found"
(typeName k) : (diag ++ diag2))
Just _ ->
if propKeysCheckOK (targetSign sign)
(typeName k) (properties k)
then (Just (makeNamed "" KeyConstr { keyConst = k }), [])
else (Nothing, mkDiag Error "property not found"
(properties k) : (diag ++ diag2))
Just _ -> if propKeysCheckOK (sourceSign sign) (typeName k) (properties k) then
(Just (makeNamed "" KeyConstr { keyConst = k }), [])
else (Nothing, mkDiag Error "property not found" (properties k) : diag)
else (Nothing, [mkDiag Error "metamodel does not exist" (sMetN ++ "-" ++ tMetN)])
propKeysCheckOK :: CSMOFSign.Sign -> String -> [PropKey] -> Bool
propKeysCheckOK _ _ [] = True
propKeysCheckOK metSign kType (ke : rest) = propKeyCheckOK metSign kType ke &&
propKeysCheckOK metSign kType rest
propKeyCheckOK :: CSMOFSign.Sign -> String -> PropKey -> Bool
propKeyCheckOK (CSMOFSign.Sign _ typRel _ _ props _ _) kType (SimpleProp pN) =
findProperty typRel props kType pN
propKeyCheckOK (CSMOFSign.Sign _ typRel _ _ props _ _) kType
(OppositeProp oppPType oppPName) =
findOppProperty typRel props kType oppPType oppPName
findProperty :: Rel.Rel CSMOFSign.TypeClass -> Set.Set CSMOFSign.PropertyT ->
String -> String -> Bool
findProperty typRel props kType pN =
let classes = kType : Set.toList (superClasses
(Rel.map CSMOFSign.name typRel) kType)
in findPropertyByTypeAndRole (Set.toList props) classes pN
findPropertyByTypeAndRole :: [CSMOFSign.PropertyT] -> [String] -> String -> Bool
findPropertyByTypeAndRole [] _ _ = False
findPropertyByTypeAndRole (p : rest) classes pN =
(elem (CSMOFSign.name (CSMOFSign.sourceType p)) classes &&
CSMOFSign.targetRole p == pN) ||
(elem (CSMOFSign.name (CSMOFSign.targetType p)) classes &&
CSMOFSign.sourceRole p == pN) ||
findPropertyByTypeAndRole rest classes pN
superClasses :: Rel.Rel String -> String -> Set.Set String
superClasses relT tc = Set.fold reach Set.empty $ Rel.succs relT tc where
reach e s = if Set.member e s then s
else Set.fold reach (Set.insert e s) $ Rel.succs relT e
findPropertyInHierarchy :: Rel.Rel CSMOFSign.TypeClass ->
Set.Set CSMOFSign.PropertyT ->
String -> String -> Maybe CSMOFSign.PropertyT
findPropertyInHierarchy typRel props kType pN =
let classes = kType : Set.toList (superClasses (Rel.map CSMOFSign.name typRel) kType)
in findPropertyElemByTypeAndRole (Set.toList props) classes pN
findPropertyElemByTypeAndRole :: [CSMOFSign.PropertyT] -> [String] -> String ->
Maybe CSMOFSign.PropertyT
findPropertyElemByTypeAndRole [] _ _ = Nothing
findPropertyElemByTypeAndRole (p : rest) classes pN =
if (elem (CSMOFSign.name (CSMOFSign.sourceType p)) classes &&
CSMOFSign.targetRole p == pN) ||
(elem (CSMOFSign.name (CSMOFSign.targetType p)) classes &&
CSMOFSign.sourceRole p == pN)
then Just p
else findPropertyElemByTypeAndRole rest classes pN
findOppProperty :: Rel.Rel CSMOFSign.TypeClass -> Set.Set CSMOFSign.PropertyT ->
String -> String -> String -> Bool
findOppProperty typRel props kType oppPType oppPName =
let classes = oppPType : Set.toList (superClasses (Rel.map CSMOFSign.name typRel)
oppPType)
in findOppPropertyByTypeAndRole (Set.toList props) classes oppPName kType
findOppPropertyByTypeAndRole :: [CSMOFSign.PropertyT] -> [String] -> String ->
String -> Bool
findOppPropertyByTypeAndRole [] _ _ _ = False
findOppPropertyByTypeAndRole (p : rest) classes pN kType =
(elem (CSMOFSign.name (CSMOFSign.sourceType p)) classes &&
CSMOFSign.targetRole p == pN &&
CSMOFSign.name (CSMOFSign.targetType p) == kType) ||
(elem (CSMOFSign.name (CSMOFSign.targetType p)) classes &&
CSMOFSign.sourceRole p == pN &&
CSMOFSign.name (CSMOFSign.sourceType p) == kType) ||
findOppPropertyByTypeAndRole rest classes pN kType
getTargetType :: String -> CSMOFSign.PropertyT -> String
getTargetType pN p = CSMOFSign.name $
if CSMOFSign.targetRole p == pN
then CSMOFSign.targetType p
else CSMOFSign.sourceType p
getOppositeType :: String -> CSMOFSign.PropertyT -> String
getOppositeType pN p = CSMOFSign.name $
if CSMOFSign.sourceRole p == pN
then CSMOFSign.targetType p
else CSMOFSign.sourceType p
third :: (String, String, CSMOFAs.Metamodel) -> CSMOFAs.Metamodel
third (_, _, c) = c
buildRules :: Sign -> (String, String, CSMOFAs.Metamodel) ->
(String, String, CSMOFAs.Metamodel) ->
[Relation] -> ([Named Sen], [Diagnosis])
buildRules sign souMet tarMet rul =
let
(rel, diag) = checkRules sign souMet tarMet rul
in (map (\ r -> makeNamed "" QVTSen { rule = r }) rel, diag)
checkRules :: Sign -> (String, String, CSMOFAs.Metamodel) ->
(String, String, CSMOFAs.Metamodel) ->
[Relation] -> ([RelationSen], [Diagnosis])
checkRules _ _ _ [] = ([], [])
checkRules sign souMet tarMet (r : rest) =
let
(rul, diag) = checkRule sign souMet tarMet r
(restRul, restDiag) = checkRules sign souMet tarMet rest
in
(rul ++ restRul, diag ++ restDiag)
checkRule :: Sign -> (String, String, CSMOFAs.Metamodel) ->
(String, String, CSMOFAs.Metamodel) -> Relation ->
([RelationSen], [Diagnosis])
checkRule sign _ _ (Relation tp rN vS prD souDom tarDom whenC whereC) =
let
rName = if tp then "Top_" ++ rN else rN
(rDefNonTop, rDiagNonTop) = case Map.lookup rN (nonTopRelations sign) of
Nothing -> (RuleDef "" False [],
[mkDiag Error "non top relation not found" rName])
Just r -> (r, [])
(rDef, rDiag) = if tp
then case Map.lookup rName (topRelations sign) of
Nothing -> (RuleDef "" False [],
[mkDiag Error "top relation not found" rName])
Just r -> (r, [])
else (RuleDef "" False [], [])
pSet = collectParSet prD souDom tarDom
vSet = collectVarSet vS prD souDom tarDom
(souPat, diagSPat) = buildPattern souDom (sourceSign sign) vSet
(tarPat, diagTPat) = buildPattern tarDom (targetSign sign) vSet
(whenCl, diagW1Pat) = checkWhenWhere whenC
(whereCl, diagW2Pat) = checkWhenWhere whereC
in
if tp
then (RelationSen rDef vSet [] souPat tarPat whenCl whereCl : -- Top Rule
[RelationSen rDefNonTop vSet pSet souPat tarPat whenCl whereCl], -- Non Top Rule
rDiag ++ rDiagNonTop ++ diagSPat ++ diagTPat ++ diagW1Pat ++ diagW2Pat)
else ([RelationSen rDefNonTop vSet pSet souPat tarPat whenCl whereCl],
rDiag ++ rDiagNonTop ++ diagSPat ++ diagTPat ++ diagW1Pat ++ diagW2Pat)
collectParSet :: [PrimitiveDomain] -> Domain -> Domain -> [RelVar]
collectParSet prD souDom tarDom =
let
prDVS = collectPrimDomVarSet prD
souVar = RelVar (domType $ template souDom) (domVar $ template souDom)
tarVar = RelVar (domType $ template tarDom) (domVar $ template tarDom)
in
[souVar, tarVar] ++ prDVS
collectVarSet :: [RelVar] -> [PrimitiveDomain] -> Domain -> Domain -> [RelVar]
collectVarSet varS prD souDom tarDom =
let
souDomVS = collectDomainVarSet souDom
tarDomVS = collectDomainVarSet tarDom
prDVS = collectPrimDomVarSet prD
in
varS ++ prDVS ++ souDomVS ++ tarDomVS
collectPrimDomVarSet :: [PrimitiveDomain] -> [RelVar]
collectPrimDomVarSet = map (\ n -> RelVar (primType n) (primName n))
collectDomainVarSet :: Domain -> [RelVar]
collectDomainVarSet dom = collectRecursiveVars (Just $ template dom)
collectRecursiveVars :: Maybe ObjectTemplate -> [RelVar]
collectRecursiveVars Nothing = []
collectRecursiveVars (Just ot) =
let otVar = RelVar (domType ot) (domVar ot)
in
otVar : foldr ((++) . collectRecursiveVars . objTemp) [] (templateList ot)
buildPattern :: Domain -> CSMOFSign.Sign -> [RelVar] -> (Pattern, [Diagnosis])
buildPattern dom sign vSet =
let
(patR, diag) = collectRecursiveRelInvoc (domVar (template dom))
(domType (template dom)) (templateList (template dom)) sign vSet
patPr = collectRecursivePreds vSet (Just $ template dom)
in
(Pattern (collectDomainVarSet dom) patR patPr, diag)
collectRecursiveRelInvoc :: String -> String -> [PropertyTemplate] ->
CSMOFSign.Sign -> [RelVar] ->
([(CSMOFSign.PropertyT, RelVar, RelVar)],
[Diagnosis])
collectRecursiveRelInvoc _ _ [] _ _ = ([], [])
collectRecursiveRelInvoc nam typ (pt : restPT) sign vSet =
case objTemp pt of
Nothing -> ([], [])
Just ot ->
let
prop = findPropertyInHierarchy (CSMOFSign.typeRel sign)
(CSMOFSign.properties sign) typ (pName pt)
(restProps, diagn) = collectRecursiveRelInvoc nam typ restPT sign vSet
(recPr, recDiag) = collectRecursiveRelInvoc (domVar ot)
(domType ot) (templateList ot) sign vSet
in
case prop of
Nothing -> ([], mkDiag Error "property not found" pt :
(diagn ++ recDiag))
Just p -> let
souV = RelVar typ nam
tarV = getVarFromTemplate pt vSet
in
case tarV of
Nothing -> (restProps ++ recPr, diagn ++ recDiag)
-- it is a OCL expression, not a variable
Just relVar -> ((p, souV, relVar) : (restProps ++ recPr),
diagn ++ recDiag)
getVarFromTemplate :: PropertyTemplate -> [RelVar] -> Maybe RelVar
getVarFromTemplate (PropertyTemplate _ ocl _) relV =
case ocl of
Nothing -> Nothing
Just (StringExp (VarExp v)) -> findVarFromName v relV
_ -> Nothing
findVarFromName :: String -> [RelVar] -> Maybe RelVar
findVarFromName _ [] = Nothing
findVarFromName nam (v : restV) = if varName v == nam
then Just v
else findVarFromName nam restV
collectRecursivePreds :: [RelVar] -> Maybe ObjectTemplate ->
[(String, String, OCL)]
collectRecursivePreds _ Nothing = []
collectRecursivePreds vSet (Just ot) =
let
tList = templateList ot
oclExps = foldr ((++) . getOclExpre (domVar ot) vSet) [] tList
in
oclExps ++ foldr ((++) . collectRecursivePreds vSet . objTemp) [] tList
getOclExpre :: String -> [RelVar] -> PropertyTemplate -> [(String, String, OCL)]
getOclExpre otN _ (PropertyTemplate pN ocl objT) =
case ocl of
Nothing -> case objT of
Nothing -> []
Just o -> [(pN, otN, StringExp (VarExp (domVar o)))]
-- ToDo Diagnosis
Just s -> [(pN, otN, s)] -- ToDo Diagnosis
checkWhenWhere :: Maybe WhenWhere -> (Maybe WhenWhere, [Diagnosis])
checkWhenWhere ww = (ww, []) -- ToDo Diagnosis
{- ToDo :: Diagnosis
las Keys no son vacias
los tipos en RelVar existen
los tipos en PrimitiveDomain existen
los nombres de variables en RelVar, PrimitiveDomain, Domain no se repiten
el domModelId del source y target Domain son los de la transformacion
los domMeta del source (de todos los obj templ) es el del source de la trans. Idem para el target
los domType del source y target existen en el source y target meta, respectivamente
los pName son propiedades que existen en cada domType
no hago nada con las oclExpre
para cada RelInVok de un WhenWhere, el nombre de la regla existe
para cada RelInvok los parametros son variables definidas y tienen los tipos de la relacion -}
-- Get every ObjectTemplate from a Domain (recursive)
getObjectTemplates :: Domain -> [ObjectTemplate]
getObjectTemplates dom = template dom : getObjectTemplatesFromOT (template dom)
getObjectTemplatesFromOT :: ObjectTemplate -> [ObjectTemplate]
getObjectTemplatesFromOT ot =
let otList = getOT $ templateList ot
in foldr ((++) . getObjectTemplatesFromOT) [] otList
getOT :: [PropertyTemplate] -> [ObjectTemplate]
getOT list =
case list of
[] -> []
el : rest -> case objTemp el of
Nothing -> getOT rest
Just typ -> typ : getOT rest