package edu.parser.QLS; import edu.Widgets; import edu.exceptions.TypeCheckException; import edu.nodes.QuestionType; import edu.nodes.styles.Style; import edu.nodes.styles.Widget; import edu.parser.QL.nodes.question.Question; import edu.parser.QLS.nodes.AbstractNode; import edu.parser.QLS.nodes.QLSIdentifier; import edu.parser.QLS.nodes.Section; import edu.parser.QLS.nodes.Stylesheet; import edu.parser.QLS.nodes.statement.Default; import edu.parser.QLS.nodes.statement.Page; import edu.parser.QLS.nodes.statement.QLSQuestion; import edu.parser.QLS.nodes.statement.Statement; import java.util.ArrayList; import java.util.List; import java.util.function.Predicate; import java.util.stream.Collectors; /** * Created by Steven Kok on 01/03/2015. */ public class TypeChecker implements QLSVisitor { public static final String UNIDENTIFIED_STYLESHEET_QUESTION = "Stylesheet contains questions that are not contained in the form. Unknown question identifiers:"; public static final String FOUND_DUPLICATE_QUESTIONS = "Found duplicate question entries in QLS for: "; private final List<QLSQuestion> stylesheetQuestions; private final List<Question> allQuestions; public TypeChecker() { this.stylesheetQuestions = new ArrayList<>(); this.allQuestions = new ArrayList<>(); } public void start(List<Question> allQuestions, Stylesheet stylesheet) { initializeLists(); this.allQuestions.addAll(allQuestions); visit(stylesheet); confirmQuestionsExistInForm(this.allQuestions); confirmNoDuplicateQuestions(stylesheetQuestions); } private void initializeLists() { this.stylesheetQuestions.clear(); this.allQuestions.clear(); } private void confirmNoDuplicateQuestions(List<QLSQuestion> stylesheetQuestions) { List<QLSQuestion> duplicateQuestions = getDuplicateQuestions(stylesheetQuestions); if (!duplicateQuestions.isEmpty()) { String duplicateQuestionsString = ListToString(duplicateQuestions); throw new TypeCheckException(FOUND_DUPLICATE_QUESTIONS + duplicateQuestionsString); } } private List<QLSQuestion> getDuplicateQuestions(List<QLSQuestion> stylesheetQuestions) { return stylesheetQuestions.stream() .filter(a -> stylesheetQuestions.stream() .filter(b -> a.getQLSIdentifier().equals(b.getQLSIdentifier())).count() > 1) .collect(Collectors.toList()); } private String ListToString(List<QLSQuestion> duplicateQuestions) { return duplicateQuestions.stream() .map(QLSQuestion::toString) .collect(Collectors.joining(", ")); } @Override public AbstractNode visit(Stylesheet stylesheet) { stylesheet.getPages() .stream() .forEach(page -> page.accept(this)); return stylesheet; } private void confirmQuestionsExistInForm(List<Question> questions) { List<QLSQuestion> notFoundQuestions = stylesheetQuestions.stream() .filter(stylesheetQuestion -> questions.stream() .noneMatch(doesFormQuestionsContainStylesheetQuestion(stylesheetQuestion))) .collect(Collectors.toList()); if (!notFoundQuestions.isEmpty()) { throw new TypeCheckException(UNIDENTIFIED_STYLESHEET_QUESTION + listToString(notFoundQuestions)); } } private Predicate<Question> doesFormQuestionsContainStylesheetQuestion(QLSQuestion stylesheetQuestion) { return question -> stylesheetQuestion.getQLSIdentifier().getIdentifier() .equals(question.getQLIdentifier().getIdentifier()); } private String listToString(List<QLSQuestion> list) { return list .stream() .map(element -> element.getQLSIdentifier().getIdentifier()) .collect(Collectors.joining(", ")); } @Override public AbstractNode visit(Page page) { page.getSections() .stream() .forEach(section -> section.accept(this)); return page; } @Override public AbstractNode visit(QLSQuestion question) { confirmQuestionHasCompatibleType(question); stylesheetQuestions.add(question); return question; } private void confirmQuestionHasCompatibleType(QLSQuestion stylesheetQuestion) { Question formQuestion = getFormQuestion(stylesheetQuestion.getQLSIdentifier().getIdentifier()); List<Widgets> supportedWidgets = formQuestion.getQuestionType().getWidgets(); boolean isWidgetTypeCompatible = isWidgetTypeCompatible(stylesheetQuestion.getStyles(), supportedWidgets); if (!isWidgetTypeCompatible) { throw new TypeCheckException("Widget type is not compatible for: " + stylesheetQuestion.getQLSIdentifier()); } } private Question getFormQuestion(String identifier) { List<Question> questionList = this.allQuestions.stream() .filter(formQuestion -> formQuestion.getQLIdentifier().getIdentifier().equals(identifier)) .collect(Collectors.toList()); if (questionList.isEmpty()) { throw new TypeCheckException("Couldn't find form question with identifier: " + identifier); } else if (questionList.size() > 1) { throw new TypeCheckException("Found duplicate form question."); } else { return questionList.get(0); } } private boolean isWidgetTypeCompatible(List<Style> styles, List<Widgets> supportedWidgets) { return styles.stream() .filter(style -> style instanceof Widget) .map(widget -> (Widget) widget) .allMatch(widget -> supportedWidgets.contains(widget.getWidget())); } @Override public AbstractNode visit(QLSIdentifier QLSIdentifier) { return QLSIdentifier; } @Override public AbstractNode visit(Section section) { visitQuestions(section.getQuestions()); visitDefaultstatements(section.getDefaultStatements()); return section; } private void visitDefaultstatements(List<Default> defaultStatements) { defaultStatements.stream() .forEach(statement -> statement.accept(this)); } private void visitQuestions(List<QLSQuestion> sections) { sections.stream() .forEach(section -> section.accept(this)); } @Override public AbstractNode visit(Default aDefault) { return aDefault; } @Override public AbstractNode visit(Style style) { return style; } @Override public AbstractNode visit(QuestionType questionType) { return questionType; } }