001package org.clafer.ast.analysis;
002
003import java.util.HashMap;
004import java.util.Map;
005import java.util.Set;
006import org.clafer.ast.AstAbstractClafer;
007import org.clafer.ast.AstClafer;
008import org.clafer.ast.AstConcreteClafer;
009import org.clafer.ast.AstException;
010import org.clafer.ast.Card;
011
012/**
013 *
014 * @author jimmy
015 */
016public class GlobalCardAnalyzer implements Analyzer {
017
018    @Override
019    public Analysis analyze(Analysis analysis) {
020        Map<AstClafer, Card> globalCardMap = new HashMap<>();
021        globalCardMap.put(analysis.getModel(), new Card(1, 1));
022
023        for (Set<AstClafer> component : analysis.getClafersInParentAndSubOrder()) {
024            for (AstClafer clafer : component) {
025                if (clafer instanceof AstConcreteClafer) {
026                    analyze((AstConcreteClafer) clafer, analysis, globalCardMap);
027                } else {
028                    analyze((AstAbstractClafer) clafer, analysis, globalCardMap);
029                }
030            }
031        }
032        return analysis.setGlobalCardMap(globalCardMap);
033    }
034
035    private static void analyze(AstAbstractClafer clafer, Analysis analysis, Map<AstClafer, Card> globalCardMap) {
036        Card globalCard = new Card(0, 0);
037        for (AstClafer sub : clafer.getSubs()) {
038            Card subGlobalCard = globalCardMap.get(sub);
039            if (subGlobalCard == null) {
040                // This is possible if a child of an abstract extends the abstract.
041                // Assume the worst possible case.
042                subGlobalCard = new Card(0, analysis.getScope(sub));
043            }
044            globalCard = globalCard.add(subGlobalCard);
045        }
046        globalCardMap.put(clafer, globalCard);
047    }
048
049    private static void analyze(AstConcreteClafer clafer, Analysis analysis, Map<AstClafer, Card> globalCardMap) {
050        Card parentGlobalCard;
051        if (!clafer.hasParent()) {
052            parentGlobalCard = new Card(1, 1);
053        } else {
054            parentGlobalCard = globalCardMap.get(clafer.getParent());
055            if (parentGlobalCard == null) {
056                // Not analyzed yet due to cycle.
057                parentGlobalCard = new Card(0, analysis.getScope(clafer.getParent()));
058            }
059        }
060        // Cap by scope
061        Card globalCard = parentGlobalCard.mult(analysis.getCard(clafer));
062        int scope = analysis.getScope(clafer);
063        if (scope < globalCard.getLow()) {
064            throw new AstException("Scope of " + clafer.getName() + " is " + scope + " which is too low, needs to be at least " + globalCard.getLow());
065        }
066        globalCard = new Card(
067                globalCard.getLow(),
068                Math.min(globalCard.getHigh(), analysis.getScope(clafer)));
069        globalCardMap.put(clafer, globalCard);
070    }
071}