package qls.semantics;
import ql.ast.form.Form;
import ql.ast.type.*;
import ql.semantics.QuestionCollector;
import ql.semantics.Questions;
import ql.semantics.errors.Messages;
import qls.ast.Page;
import qls.ast.rule.*;
import qls.ast.statement.*;
import qls.ast.Stylesheet;
import qls.ast.StylesheetVisitor;
import qls.ast.statement.Question;
import qls.ast.statement.Statement;
import qls.semantics.messages.StyleError;
import java.util.*;
/**
* Created by bore on 03/03/15.
*/
public class TypeChecker implements StylesheetVisitor<Void>, StatementVisitor<Void>
{
private final Questions questions;
private final Set<String> refQuestions;
private final Messages messages;
public static Messages check(Stylesheet s, Form f)
{
Questions questions = QuestionCollector.collect(f);
TypeChecker checker = new TypeChecker(questions);
s.accept(checker);
checker.allQuestionsReferencedCheck();
return checker.messages;
}
private TypeChecker(Questions questions)
{
this.questions = questions;
this.refQuestions = new HashSet<>();
this.messages = new Messages();
}
@Override
public Void visit(Stylesheet s)
{
for (Page p : s.getBody())
{
p.accept(this);
}
return null;
}
@Override
public Void visit(Page p)
{
for (Statement s : p.getBody())
{
s.accept(this);
}
return null;
}
@Override
public Void visit(Section s)
{
for (Statement stat : s.getBody())
{
stat.accept(this);
}
return null;
}
@Override
public Void visit(Question q)
{
this.addQuestionReference(q);
return null;
}
@Override
public Void visit(QuestionWithRules q)
{
this.addQuestionReference(q);
if (this.questions.contains(q.getId()))
{
Rules rs = q.getBody();
Type qType = this.questions.getType(q.getId());
this.checkForWidgetTypeMismatch(rs, qType, q.getLineNumber());
}
return null;
}
@Override
public Void visit(DefaultStat d)
{
Rules rs = d.getBody();
Type type = d.getType();
this.checkForWidgetTypeMismatch(rs, type, d.getLineNumber());
return null;
}
private void addQuestionReference(Question q)
{
String id = q.getId();
if (this.isQuestionNotDefinedInForm(q))
{
this.messages.add(StyleError.undefinedQuestion(id, q.getLineNumber()));
}
if (this.isQuestionAlreadyReferenced(q))
{
this.messages.add(StyleError.questionAlreadyReferenced(id, q.getLineNumber()));
}
this.refQuestions.add(id);
}
private void checkForWidgetTypeMismatch(Rules rs, Type declType, Integer lineNumber)
{
for (Rule r : rs)
{
if (!(r.isCompatibleWithType(declType)))
{
this.messages.add(StyleError.widgetTypeMismatch(declType.getTitle(), lineNumber));
}
}
}
private boolean isQuestionNotDefinedInForm(Question q)
{
return !(this.questions.contains(q.getId()));
}
private boolean isQuestionAlreadyReferenced(Question q)
{
return this.refQuestions.contains(q.getId());
}
private void allQuestionsReferencedCheck()
{
for (String id : this.questions)
{
if (this.isQuestionNotReferenced(id))
{
this.messages.add(StyleError.questionNotReferenced(id));
}
}
}
private boolean isQuestionNotReferenced(String id)
{
return !(this.refQuestions.contains(id));
}
}