package org.nlamah.QLS.Builders;
import java.awt.Color;
import java.awt.Font;
import java.awt.GraphicsEnvironment;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.antlr.v4.runtime.tree.ParseTree;
import org.nlamah.QBase.Constants.QBaseQuestionType;
import org.nlamah.QBase.Error.EnumRecognitionError;
import org.nlamah.QBase.Error.QBaseParsingError;
import org.nlamah.QBase.Model.QBaseNode;
import org.nlamah.QBase.Tools.QLSTools;
import org.nlamah.QBase.Tools.StringTools;
import org.nlamah.QL.Model.Expression.Literal.BooleanLiteral;
import org.nlamah.QL.Model.Expression.Literal.NumberLiteral;
import org.nlamah.QL.Model.Expression.Literal.TextLiteral;
import org.nlamah.QLS.QLSBaseVisitor;
import org.nlamah.QLS.QLSParser;
import org.nlamah.QLS.QLSParser.DefaultBlockContext;
import org.nlamah.QLS.QLSParser.PageContext;
import org.nlamah.QLS.QLSParser.SectionContext;
import org.nlamah.QLS.QLSParser.StyleDeclarationContext;
import org.nlamah.QLS.QLSParser.StylesheetBlockContext;
import org.nlamah.QLS.Error.DoublePropertyDeclarationError;
import org.nlamah.QLS.Error.FontRecognitionError;
import org.nlamah.QLS.Model.Abstract.QLSNode;
import org.nlamah.QLS.Model.Abstract.SectionItem;
import org.nlamah.QLS.Model.Abstract.StyleDeclaration;
import org.nlamah.QLS.Model.Declaration.ColorDeclaration;
import org.nlamah.QLS.Model.Declaration.FontDeclaration;
import org.nlamah.QLS.Model.Declaration.FontSizeDeclaration;
import org.nlamah.QLS.Model.Declaration.WidgetDeclaration;
import org.nlamah.QLS.Model.Declaration.WidthDeclaration;
import org.nlamah.QLS.Model.StylesheetBlock.DefaultBlock;
import org.nlamah.QLS.Model.StylesheetBlock.Page;
import org.nlamah.QLS.Model.StylesheetBlock.StyleBlock;
import org.nlamah.QLS.Model.StylesheetBlock.StyledQuestion;
import org.nlamah.QLS.Model.StylesheetBlock.Stylesheet;
import org.nlamah.QLS.Model.StylesheetBlock.Section;
import org.nlamah.QLS.Model.Value.IdentifierValue;
import org.nlamah.QLS.Model.Value.WidgetTypeEnum;
public class RawStylesheetBuilder extends QLSBaseVisitor<QLSNode>
{
private List<QBaseParsingError> errors;
private int sectionDepthLevel = -1;
public RawStylesheetBuilder()
{
errors = new ArrayList<QBaseParsingError>();
}
public List<QBaseParsingError> errors()
{
return this.errors;
}
public Stylesheet build(ParseTree tree)
{
return (Stylesheet) tree.accept(this);
}
private void checkForDoublePropertyDeclarations(StyleDeclaration styleDeclaration, List<StyleDeclaration> styleDeclarations)
{
List<StyleDeclaration> foundDeclarations = QLSTools.findStyleDeclarationsOfTheSameClass(styleDeclaration, styleDeclarations);
if (foundDeclarations.size() > 0)
{
foundDeclarations.add(styleDeclaration);
errors.add(new DoublePropertyDeclarationError(foundDeclarations));
}
}
@Override
public QLSNode visitStylesheet(QLSParser.StylesheetContext ctx)
{
IdentifierValue identifier = new IdentifierValue(ctx.Identifier().getText());
QBaseNode.addSourceCodePosition(identifier, ctx);
List<Page> pages = new ArrayList<Page>();
for (PageContext contextualPage : ctx.page())
{
Page page = (Page) contextualPage.accept(this);
pages.add(page);
}
List<DefaultBlock> defaultBlocks = new ArrayList<DefaultBlock>();
for (DefaultBlockContext contextualDefaultBlock : ctx.defaultBlock())
{
DefaultBlock defaultBlock = (DefaultBlock) contextualDefaultBlock.accept(this);
defaultBlocks.add(defaultBlock);
}
Stylesheet stylesheet = new Stylesheet(identifier, pages, defaultBlocks);
QBaseNode.addSourceCodePosition(stylesheet, ctx);
return stylesheet;
}
@Override
public QLSNode visitPage(QLSParser.PageContext ctx)
{
IdentifierValue identifier = new IdentifierValue(ctx.Identifier().getText());
QBaseNode.addSourceCodePosition(identifier, ctx);
List<Section> sections = new ArrayList<Section>();
for (SectionContext contextualSection : ctx.section())
{
Section section = (Section) contextualSection.accept(this);
sections.add(section);
}
List<DefaultBlock> defaultBlocks = new ArrayList<DefaultBlock>();
for (DefaultBlockContext contextualDefaultBlock : ctx.defaultBlock())
{
DefaultBlock defaultBlock = (DefaultBlock) contextualDefaultBlock.accept(this);
defaultBlocks.add(defaultBlock);
}
Page page = new Page(identifier, sections, defaultBlocks);
QBaseNode.addSourceCodePosition(page, ctx);
return page;
}
@Override
public QLSNode visitSection(QLSParser.SectionContext ctx)
{
sectionDepthLevel++;
String title = StringTools.removeSurroundingQuotes(ctx.Text().getText());
List<SectionItem> sectionItems = new ArrayList<SectionItem>();
for (StylesheetBlockContext contextualSection : ctx.stylesheetBlock())
{
SectionItem sectionItem = (SectionItem) contextualSection.accept(this);
sectionItems.add(sectionItem);
}
List<DefaultBlock> defaultBlocks = new ArrayList<DefaultBlock>();
for (DefaultBlockContext contextualDefaultBlock : ctx.defaultBlock())
{
DefaultBlock defaultBlock = (DefaultBlock) contextualDefaultBlock.accept(this);
defaultBlocks.add(defaultBlock);
}
Section section = new Section(title, sectionItems, defaultBlocks, sectionDepthLevel);
QBaseNode.addSourceCodePosition(section, ctx);
sectionDepthLevel--;
return section;
}
@Override
public QLSNode visitStyledQuestion(QLSParser.StyledQuestionContext ctx)
{
IdentifierValue identifier = new IdentifierValue(ctx.Identifier().getText());
QBaseNode.addSourceCodePosition(identifier, ctx);
List<StyleDeclaration> styleDeclarations = new ArrayList<StyleDeclaration>();
for (StyleDeclarationContext contextualStyleDeclaration : ctx.styleDeclaration())
{
StyleDeclaration styleDeclaration = (StyleDeclaration)contextualStyleDeclaration.accept(this);
checkForDoublePropertyDeclarations(styleDeclaration, styleDeclarations);
styleDeclarations.add(styleDeclaration);
}
StyleBlock styleBlock = new StyleBlock(styleDeclarations);
QBaseNode.addSourceCodePosition(styleBlock, ctx);
StyledQuestion styledQuestion = new StyledQuestion(identifier, styleBlock);
QBaseNode.addSourceCodePosition(styledQuestion, ctx);
return styledQuestion;
}
@Override
public QLSNode visitDefaultBlock(QLSParser.DefaultBlockContext ctx)
{
String questionTypeString = ctx.QuestionType() != null ? ctx.QuestionType().getText().toUpperCase() : null;
QBaseQuestionType questionType = null;
if (questionTypeString != null)
{
try
{
questionType = QBaseQuestionType.valueOf(questionTypeString);
}
catch(Exception ex)
{
errors.add(new EnumRecognitionError(questionTypeString, ctx.getStart().getLine(), ctx.getStart().getCharPositionInLine()));
}
}
List<StyleDeclaration> styleDeclarations = new ArrayList<StyleDeclaration>();
for (StyleDeclarationContext contextualStyleDeclaration : ctx.styleDeclaration())
{
StyleDeclaration styleDeclaration = (StyleDeclaration)contextualStyleDeclaration.accept(this);
checkForDoublePropertyDeclarations(styleDeclaration, styleDeclarations);
styleDeclarations.add(styleDeclaration);
}
DefaultBlock defaultBlock = new DefaultBlock(questionType, styleDeclarations);
QBaseNode.addSourceCodePosition(defaultBlock, ctx);
return defaultBlock;
}
@Override
public QLSNode visitWidthDeclaration(QLSParser.WidthDeclarationContext ctx)
{
String numberValueString = ctx.Number().getText();
int numberValue = Integer.parseInt(numberValueString);
WidthDeclaration widthDeclaration = new WidthDeclaration(numberValue);
QBaseNode.addSourceCodePosition(widthDeclaration, ctx);
return widthDeclaration;
}
@Override
public QLSNode visitFontDeclaration(QLSParser.FontDeclarationContext ctx)
{
String fontValueString = StringTools.removeSurroundingQuotes(ctx.Text().getText());
GraphicsEnvironment graphicsEnvironment = GraphicsEnvironment.getLocalGraphicsEnvironment();
String[] availableFontFamilyNames = graphicsEnvironment.getAvailableFontFamilyNames();
if (Arrays.asList(availableFontFamilyNames).indexOf(fontValueString) < 0)
{
errors.add(new FontRecognitionError(fontValueString, ctx.getStart().getLine(), ctx.getStart().getCharPositionInLine()));
}
Font font = Font.decode(fontValueString);
FontDeclaration fontDeclaration = new FontDeclaration(font);
QBaseNode.addSourceCodePosition(fontDeclaration, ctx);
return fontDeclaration;
}
@Override
public QLSNode visitFontSizeDeclaration(QLSParser.FontSizeDeclarationContext ctx)
{
String numberValueString = ctx.Number().getText();
int numberValue = Integer.parseInt(numberValueString);
FontSizeDeclaration fontSizeDeclaration = new FontSizeDeclaration(numberValue);
QBaseNode.addSourceCodePosition(fontSizeDeclaration, ctx);
return fontSizeDeclaration;
}
@Override
public QLSNode visitColorDeclaration(QLSParser.ColorDeclarationContext ctx)
{
String hexNumberValueString = ctx.HexNumber().getText();
hexNumberValueString = QLSTools.uniformHexNumberString(hexNumberValueString);
Color color = Color.decode(hexNumberValueString);
ColorDeclaration colorDeclaration = new ColorDeclaration(color);
QBaseNode.addSourceCodePosition(colorDeclaration, ctx);
return colorDeclaration;
}
@Override
public QLSNode visitWidgetDeclaration(QLSParser.WidgetDeclarationContext ctx)
{
WidgetDeclaration widgetDeclaration = (WidgetDeclaration) ctx.widgetStyle().accept(this);
QBaseNode.addSourceCodePosition(widgetDeclaration, ctx);
return widgetDeclaration;
}
@Override
public QLSNode visitCheckBox(QLSParser.CheckBoxContext ctx)
{
WidgetDeclaration widgetDeclaration = new WidgetDeclaration(WidgetTypeEnum.CHECKBOX, QBaseQuestionType.BOOLEAN, null);
QBaseNode.addSourceCodePosition(widgetDeclaration, ctx);
return widgetDeclaration;
}
@Override
public QLSNode visitSpinBox(QLSParser.SpinBoxContext ctx)
{
WidgetDeclaration widgetDeclaration = new WidgetDeclaration(WidgetTypeEnum.SPINBOX, QBaseQuestionType.NUMBER, null);
QBaseNode.addSourceCodePosition(widgetDeclaration, ctx);
return widgetDeclaration;
}
@Override
public QLSNode visitRadioButtonText(QLSParser.RadioButtonTextContext ctx)
{
Map<TextLiteral, TextLiteral> answers = new LinkedHashMap<TextLiteral, TextLiteral>();
for (org.antlr.v4.runtime.Token contextualTextValue : ctx.answer)
{
TextLiteral answer = new TextLiteral(StringTools.removeSurroundingQuotes(contextualTextValue.getText()));
QBaseNode.addSourceCodePosition(answer, ctx);
answers.put(answer, answer);
}
WidgetDeclaration widgetDeclaration = new WidgetDeclaration(WidgetTypeEnum.RADIOBUTTON, QBaseQuestionType.TEXT, answers);
QBaseNode.addSourceCodePosition(widgetDeclaration, ctx);
return widgetDeclaration;
}
@Override
public QLSNode visitRadioButtonNumber(QLSParser.RadioButtonNumberContext ctx)
{
Map<TextLiteral, NumberLiteral> answers = new LinkedHashMap<TextLiteral, NumberLiteral>();
for (org.antlr.v4.runtime.Token contextualNumberValue : ctx.answer)
{
String numberValueString = StringTools.removeSurroundingQuotes(contextualNumberValue.getText());
TextLiteral answer = new TextLiteral(numberValueString);
NumberLiteral answerValue = new NumberLiteral(Integer.valueOf(numberValueString));
QBaseNode.addSourceCodePosition(answer, ctx);
QBaseNode.addSourceCodePosition(answerValue, ctx);
answers.put(answer, answerValue);
}
WidgetDeclaration widgetDeclaration = new WidgetDeclaration(WidgetTypeEnum.RADIOBUTTON, QBaseQuestionType.NUMBER, answers);
QBaseNode.addSourceCodePosition(widgetDeclaration, ctx);
return widgetDeclaration;
}
@Override
public QLSNode visitRadioButtonBoolean(QLSParser.RadioButtonBooleanContext ctx)
{
Map<TextLiteral, BooleanLiteral> answers = new LinkedHashMap<TextLiteral, BooleanLiteral>();
for (org.antlr.v4.runtime.Token contextualBooleanValue : ctx.answer)
{
String booleanValueString = StringTools.removeSurroundingQuotes(contextualBooleanValue.getText());
TextLiteral answer = new TextLiteral(booleanValueString);
BooleanLiteral answerValue = new BooleanLiteral(booleanValueString.equals("yes") ? true : false);
QBaseNode.addSourceCodePosition(answer, ctx);
QBaseNode.addSourceCodePosition(answerValue, ctx);
answers.put(answer, answerValue);
}
WidgetDeclaration widgetDeclaration = new WidgetDeclaration(WidgetTypeEnum.RADIOBUTTON, QBaseQuestionType.BOOLEAN, answers);
QBaseNode.addSourceCodePosition(widgetDeclaration, ctx);
return widgetDeclaration;
}
@Override
public QLSNode visitTextField(QLSParser.TextFieldContext ctx)
{
return null;
}
@Override
public QLSNode visitNumberField(QLSParser.NumberFieldContext ctx)
{
return null;
}
}