package org.fugazi.qls.type_checker;
import org.fugazi.ql.ast.form.form_data.QLFormDataStorage;
import org.fugazi.ql.ast.statement.Question;
import org.fugazi.ql.ast.type.Type;
import org.fugazi.ql.type_checker.issue.ASTIssueHandler;
import org.fugazi.ql.type_checker.issue.ASTNodeIssue;
import org.fugazi.qls.ast.question.QLSQuestion;
import org.fugazi.qls.ast.style.DefaultStyleDeclaration;
import org.fugazi.qls.ast.stylesheet.stylesheet_data.QLSStyleSheetDataStorage;
import org.fugazi.qls.ast.widget.AbstractQLSWidget;
import org.fugazi.qls.ast.widget.widget_types.IWidgetType;
import org.fugazi.qls.ast.widget.widget_types.WidgetTypeToWidgetVisitor;
import org.fugazi.qls.type_checker.issue.ASTQlsNodeIssueType;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
public class QLSTypeChecker {
private QLFormDataStorage qlFormData;
private QLSStyleSheetDataStorage qlsStyleSheetData;
private final ASTIssueHandler astIssueHandler;
public QLSTypeChecker(
QLSStyleSheetDataStorage _qlsSheetData, QLFormDataStorage _qlFormData)
{
this.qlsStyleSheetData = _qlsSheetData;
this.qlFormData = _qlFormData;
this.astIssueHandler = new ASTIssueHandler();
}
/**
* =====================
* Private main check methods
* =====================
*/
private void checkForUndefinedQuestions() {
List<Question> qlQuestions = this.qlFormData.getAllQuestions();
List<String> qlQuestionIdNames = this.getQlQuestionIdNames(qlQuestions);
List<QLSQuestion> qlsQuestions = this.qlsStyleSheetData.getQuestions();
for (QLSQuestion question : qlsQuestions ) {
if (!qlQuestionIdNames.contains(question.getIdName())) {
this.astIssueHandler.registerNewError(
ASTQlsNodeIssueType.QLS_ERROR.UNDEFINED, question,
"Attempted to define style for an undefined question "
+ question.getIdName() + "."
);
}
}
}
private void checkIfAllQuestionsPlaced() {
List<Question> qlQuestions = this.qlFormData.getAllQuestions();
List<QLSQuestion> qlsQuestions = this.qlsStyleSheetData.getQuestions();
List<String> qlsQuestionIdNames = this.getQlsQuestionIdNames(qlsQuestions);
for (Question question : qlQuestions ) {
if (!qlsQuestionIdNames.contains(question.getIdName())) {
this.astIssueHandler.registerNewError(
ASTQlsNodeIssueType.QLS_ERROR.MISSING_STYLE, question,
"QL Question " + question.getIdName() +
" not placed by QLS sheet and missing style definition."
);
}
}
}
private void checkForMultipleQuestionPlacements() {
List<QLSQuestion> qlsQuestions = this.qlsStyleSheetData.getQuestions();
List<String> qlsQuestionIdNames = new ArrayList<>();
for (QLSQuestion question : qlsQuestions) {
if (qlsQuestionIdNames.contains(question.getIdName())) {
this.astIssueHandler.registerNewError(
ASTQlsNodeIssueType.QLS_ERROR.DUPLICATE, question,
"QLS Question " + question.getIdName() +
" already defined (duplicate)."
);
} else {
qlsQuestionIdNames.add(question.getIdName());
}
}
}
private void checkWidgetTypeCompatibilityInQuestion() {
List<QLSQuestion> qlsQuestions = this.qlsStyleSheetData.getQuestions();
HashMap<String, Type> questionTypes = this.qlFormData.getallQuestionTypes();
for (QLSQuestion question : qlsQuestions) {
AbstractQLSWidget questionWidget = question.getWidget();
List<Type> supportedQuestionTypes = questionWidget.getSupportedQuestionTypes();
Type questionType = questionTypes.get(question.getIdName());
// if questionType == null that means question is not
// contained in the qlForm, is reported as QLS_ERROR.UNDEFINED
if (questionType != null && !supportedQuestionTypes.contains(questionType)) {
this.astIssueHandler.registerNewError(
ASTQlsNodeIssueType.QLS_ERROR.WRONG_WIDGET_TYPE, question,
"Wrong widget " + questionWidget + " for question type "
+ questionType + "."
);
}
}
}
private void checkWidgetTypeCompatibilityInDeclarations() {
List<DefaultStyleDeclaration> defaultDeclarations = this.qlsStyleSheetData.getDefaultStyleDeclarations();
WidgetTypeToWidgetVisitor widgetTypeToWidgetVisitor = new WidgetTypeToWidgetVisitor();
for (DefaultStyleDeclaration defaultStyleDeclaration : defaultDeclarations) {
IWidgetType widgetType = defaultStyleDeclaration.getWidgetType();
// Convert form type to widget.
AbstractQLSWidget widget = widgetType.accept(widgetTypeToWidgetVisitor);
List<Type> supportedWidgetTypes = widget.getSupportedQuestionTypes();
Type questionType = defaultStyleDeclaration.getQuestionType();
if (!supportedWidgetTypes.contains(questionType)) {
this.astIssueHandler.registerNewError(
ASTQlsNodeIssueType.QLS_ERROR.WRONG_WIDGET_TYPE, defaultStyleDeclaration,
"Wrong widget in Default Style Declaration " + widget
+ " for question type " + questionType + "."
);
}
}
}
/**
* =====================
* Helper check methods
* =====================
*/
private List<String> getQlQuestionIdNames(List<Question> _qlQuestions) {
List<String> qlQuestionIdNames = new ArrayList<>();
for (Question question : _qlQuestions) {
qlQuestionIdNames.add(question.getIdName());
}
return qlQuestionIdNames;
}
private List<String> getQlsQuestionIdNames(List<QLSQuestion> _qlsQuestions) {
List<String> qlsQuestionIdNames = new ArrayList<>();
for (QLSQuestion question : _qlsQuestions) {
qlsQuestionIdNames.add(question.getIdName());
}
return qlsQuestionIdNames;
}
/**
* =====================
* Exposed global methods
* =====================
*/
public boolean checkStylesheet() {
// clear errors and warnings
// (so that multiple checks can be performed on one instance)
this.clearErrorsAndWarnings();
this.checkForUndefinedQuestions();
this.checkIfAllQuestionsPlaced();
this.checkForMultipleQuestionPlacements();
this.checkWidgetTypeCompatibilityInQuestion();
this.checkWidgetTypeCompatibilityInDeclarations();
return this.isFormCorrect();
}
public boolean isFormCorrect() {
return !this.hasErrors();
}
public boolean hasErrors() {
return (this.astIssueHandler.hasErrors());
}
public List<ASTNodeIssue> getErrors() {
return this.astIssueHandler.getErrors();
}
public List<ASTNodeIssue> getWarnings() {
return this.astIssueHandler.getWarnings();
}
public void clearErrorsAndWarnings() {
this.astIssueHandler.clearErrorsAndWarnings();
}
}