package qlProject.typeChecking.typeCheckVisitors; import java.util.*; import qlProject.ast.Questionnaire; import qlProject.ast.statement.IStatement; import qlProject.ast.statement.IStatementsVisitor; import qlProject.ast.statement.IfStatement; import qlProject.ast.statement.assignment.Assignment; import qlProject.ast.statement.assignment.ComputedAssignment; import qlProject.ast.statement.assignment.DirectAssignment; import qlProject.ast.type.Type; import qlProject.ast.value.Value; import qlProject.gui.gui_building_visitors.DefaultValueVisitor; import qlProject.typeChecking.complaints.Complaint; import qlProject.typeChecking.complaints.expression_level_complaint.CyclicReferenceError; import qlProject.typeChecking.complaints.statement_level_complaint.DuplicateLabelsWarning; import qlProject.typeChecking.complaints.statement_level_complaint.TypeClashingDuplicateQsError; import qlProject.util.QuestionDetails; public class QuestionsStoringVisitor implements IStatementsVisitor { private final Map<String,Set<String>> deps; private final List<String> labelsEnvironment = new ArrayList<String>(); private final HashSet<Complaint> complaints; private final Map<String,Set<String>> awaitingCalculationIds; private final Map<String, QuestionDetails> questionsDetails; public QuestionsStoringVisitor(Map<String, QuestionDetails> questionsDetails, Map<String, Value> valuesEnvironment, Map<String, Set<String>> deps, HashSet<Complaint> complaints, Map<String,Set<String>> awaitingCalculationIds) { this.questionsDetails = questionsDetails; this.deps = deps; this.complaints = complaints; this.awaitingCalculationIds = awaitingCalculationIds; } @Override public void visit(Questionnaire q) { for (IStatement statement : q.getStatements()){ statement.accept(this); } } @Override public void visit(IfStatement ifBox){ for (IStatement question : ifBox.getIfTrueStatements()){ question.accept(this); } for (IStatement question : ifBox.getIfFalseStatements()){ question.accept(this); } } public void addDependency(String observer, String observed,Map<String,Set<String>> depMap){ if (depMap.containsKey(observer)){ if (!(depMap.get(observer).contains(observed))){// a set sod can be removed depMap.get(observer).add(observed); } } else {depMap.put(observer, new HashSet<String>()); depMap.get(observer).add(observed); } } public void documentExpressionVariables(ComputedAssignment a, Map<String, Set<String>> dependenciesMap) { for (String observed : a.getExpressionVariables()){ addDependency(a.getId(), observed, dependenciesMap); } } @Override public void visit (ComputedAssignment a){ documentExpressionVariables(a, deps); processAssignment(a); } @Override public void visit (DirectAssignment a){ processAssignment(a); } public void processAssignment(Assignment a){ checkTypeClash(a); checkExistingLabel(a); storeToEnvironments(a); } public Value getDefaultValue(Type t){ return (Value)t.accept(new DefaultValueVisitor()); } public void storeToEnvironments(Assignment a){ Value v = getDefaultValue(a.getType()); QuestionDetails qDetails = new QuestionDetails(a, a.getId(), a.getQuestionText(),a.getType(),v); questionsDetails.put(a.getId(), qDetails); labelsEnvironment.add(a.getQuestionText()); } public void checkExistingLabel(Assignment q){ if (labelsEnvironment.contains(q.getQuestionText())){ complaints.add(new DuplicateLabelsWarning (q.getQuestionText())); } } public void checkTypeClash(Assignment a){ if (questionsDetails.containsKey(a.getId()) && !(a.getType().equals(a.getType()))){ complaints.add(new TypeClashingDuplicateQsError (a, questionsDetails.get(a.getId()).getQuestion())); } } /* The following methods extract the indirect dependencies between questions * from <a,questionsA> -> <b,questionsB> <a,questionsA + > * if questionsA[i] is b then questionsB are added to the collection questionsA * possible problems of modifying a collection while it is being iterated over are solved * by storing them in * temporary collections and after the loop adding them * the **** collection **** * */ public void extractTransitiveClosureDependencies(){ Map<String, Set<String>> indirectDependencies = new HashMap<String, Set<String>>(); Map<String, Set<String>> intermediatingDependencies = new HashMap<String, Set<String>>(); for(String observer : deps.keySet()){ checkCyclicDependency(observer); for (String observedByObserver : deps.get(observer)){ addTemporaryTransitiveReference(indirectDependencies, intermediatingDependencies, observer, observedByObserver); } } updateIndirectDependencies(indirectDependencies); updateIntermediatingDependencies(intermediatingDependencies); // TODO document the steps if (!indirectDependencies.isEmpty()){ extractTransitiveClosureDependencies(); } } public void checkCyclicDependency(String id){ if (deps.get(id).contains(id)){ complaints.add(new CyclicReferenceError(id)); } } public void updateIndirectDependencies(Map<String, Set<String>> dMap){ for (String observer : dMap.keySet()){ deps.get(observer).addAll(dMap.get(observer)); } } public void updateIntermediatingDependencies(Map<String, Set<String>> dMap){ for (String observer : dMap.keySet()){ deps.get(observer).removeAll(dMap.get(observer)); } } public void addTemporaryTransitiveReference (Map<String, Set<String>> indirect, Map<String, Set<String>> intermediate, String observer, String observed){ if (deps.keySet().contains(observed)){ addIndirectDependencies(indirect, observer, observed); addIntermediaitingDependency(intermediate, observer, observed); addAwaitingCalculationId(observer, observed); } } public void addIndirectDependencies(Map<String, Set<String>> dMap, String observer, String observed){ if (!dMap.keySet().contains(observer)) {dMap.put(observer, new HashSet<String>());} dMap.get(observer).addAll(deps.get(observed)); } public void addIntermediaitingDependency(Map<String, Set<String>> dMap, String observer, String observed){ if (!dMap.keySet().contains(observer)) {dMap.put(observer, new HashSet<String>());} // TODO wrong - no put dMap.get(observer).add(observed); } public void addAwaitingCalculationId(String observer, String observed){ if (!awaitingCalculationIds.keySet().contains(observed)) {awaitingCalculationIds.put(observed, new HashSet<String>());} awaitingCalculationIds.get(observed).add(observer); } }