com.github.jknack.css.CSSBuilder.java Source code

Java tutorial

Introduction

Here is the source code for com.github.jknack.css.CSSBuilder.java

Source

/**
 * Copyright (c) 2012-2013 Edgar Espina
 *
 * This file is part of css.java.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.github.jknack.css;

import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;

import org.antlr.v4.runtime.Token;
import org.antlr.v4.runtime.tree.ParseTree;
import org.antlr.v4.runtime.tree.TerminalNode;

import com.github.jknack.css.expression.ExpressionList;
import com.github.jknack.css.expression.FunctionExpression;
import com.github.jknack.css.expression.HexColorExpression;
import com.github.jknack.css.expression.IdExpression;
import com.github.jknack.css.expression.NumberExpression;
import com.github.jknack.css.expression.StringExpression;
import com.github.jknack.css.expression.URLExpression;
import com.github.jknack.css.internal.CssBaseVisitor;
import com.github.jknack.css.internal.CssParser.AttributeContext;
import com.github.jknack.css.internal.CssParser.BlockContext;
import com.github.jknack.css.internal.CssParser.CharSetContext;
import com.github.jknack.css.internal.CssParser.ClassSelectorContext;
import com.github.jknack.css.internal.CssParser.CombinatorContext;
import com.github.jknack.css.internal.CssParser.DeclarationContext;
import com.github.jknack.css.internal.CssParser.ExpressionContext;
import com.github.jknack.css.internal.CssParser.FunctionContext;
import com.github.jknack.css.internal.CssParser.HexColorExprContext;
import com.github.jknack.css.internal.CssParser.IdExprContext;
import com.github.jknack.css.internal.CssParser.IdSelectorContext;
import com.github.jknack.css.internal.CssParser.IdentNamespaceTypeSelectorContext;
import com.github.jknack.css.internal.CssParser.IdentNamespaceUniversalSelectorContext;
import com.github.jknack.css.internal.CssParser.NegationContext;
import com.github.jknack.css.internal.CssParser.NonamespaceTypeSelectorContext;
import com.github.jknack.css.internal.CssParser.NonamespaceUniversalSelectorContext;
import com.github.jknack.css.internal.CssParser.NumberExprContext;
import com.github.jknack.css.internal.CssParser.PseudoContext;
import com.github.jknack.css.internal.CssParser.RuleSetContext;
import com.github.jknack.css.internal.CssParser.SelectorContext;
import com.github.jknack.css.internal.CssParser.SelectorGroupContext;
import com.github.jknack.css.internal.CssParser.SelectorTypeContext;
import com.github.jknack.css.internal.CssParser.StatementContext;
import com.github.jknack.css.internal.CssParser.StringExprContext;
import com.github.jknack.css.internal.CssParser.StyleSheetContext;
import com.github.jknack.css.internal.CssParser.TermContext;
import com.github.jknack.css.internal.CssParser.TypeSelectorContext;
import com.github.jknack.css.internal.CssParser.UniversalNamepaceUniversalSelectorContext;
import com.github.jknack.css.internal.CssParser.UniversalSelectorContext;
import com.github.jknack.css.internal.CssParser.UnivesalNamespaceTypeSelectorContext;
import com.github.jknack.css.internal.CssParser.UrlExprContext;
import com.github.jknack.css.selector.AbstractSelector;
import com.github.jknack.css.selector.AttributeSelector;
import com.github.jknack.css.selector.AttributeSelector.ValueSelector;
import com.github.jknack.css.selector.ChildCombinator;
import com.github.jknack.css.selector.ClassSelector;
import com.github.jknack.css.selector.DescendantCombinator;
import com.github.jknack.css.selector.GeneralSiblingCombinator;
import com.github.jknack.css.selector.IdSelector;
import com.github.jknack.css.selector.NotSelector;
import com.github.jknack.css.selector.PseudoSelector;
import com.github.jknack.css.selector.SiblingCombinator;
import com.github.jknack.css.selector.TypeSelector;
import com.github.jknack.css.selector.UniversalSelector;

class CSSBuilder extends CssBaseVisitor<Object> {

    @Override
    public Object visit(final ParseTree tree) {
        return super.visit(tree);
    }

    @Override
    public Charset visitCharSet(final CharSetContext ctx) {
        String charSet = text(ctx.STRING());
        charSet = charSet.substring(1, charSet.length() - 1);
        return Charset.forName(charSet);
    }

    @Override
    public Object visitStyleSheet(final StyleSheetContext ctx) {
        StyleSheet sheet = new StyleSheet();
        // charset
        if (ctx.charSet() != null) {
            sheet.charset(visitCharSet(ctx.charSet()));
        }
        for (StatementContext statementCtx : ctx.statement()) {
            Object candidate = visitChildren(statementCtx);
            if (candidate instanceof Rule) {
                sheet.add((Rule) candidate);
            }
        }
        return sheet;
    }

    @Override
    public Object visitRuleSet(final RuleSetContext ctx) {
        Rule rule = new Rule();
        List<Selector> selectors = visitSelectorGroup(ctx.selectorGroup());
        for (Selector selector : selectors) {
            rule.add(selector);
        }
        visitBlock(rule, ctx.block());
        return rule;
    }

    private void visitBlock(final Rule rule, final BlockContext block) {
        for (DeclarationContext declarationCtx : block.declaration()) {
            String name = text(declarationCtx.IDENT());
            Expression expression = visitExpression(declarationCtx.expression());
            rule.property(name, expression);
        }
    }

    @Override
    public Expression visitExpression(final ExpressionContext ctx) {
        Expression expr = (Expression) visit(ctx.left);
        if (ctx.right != null && ctx.right.size() > 0) {
            ExpressionList list = new ExpressionList();
            list.add(expr);
            for (TermContext right : ctx.right) {
                list.add((Expression) visit(right));
            }
            expr = list;
        }
        return expr;
    }

    @Override
    public Object visitNumberExpr(final NumberExprContext ctx) {
        return new NumberExpression(ctx.getText());
    }

    @Override
    public Object visitStringExpr(final StringExprContext ctx) {
        return new StringExpression(ctx.getText());
    }

    @Override
    public Object visitIdExpr(final IdExprContext ctx) {
        return new IdExpression(ctx.getText());
    }

    @Override
    public Object visitUrlExpr(final UrlExprContext ctx) {
        return new URLExpression(ctx.getText());
    }

    @Override
    public Object visitHexColorExpr(final HexColorExprContext ctx) {
        return new HexColorExpression(ctx.getText());
    }

    @Override
    public Object visitFunction(final FunctionContext ctx) {
        return new FunctionExpression(text(ctx.IDENT()), (Expression) visit(ctx.expression()));
    }

    @Override
    public List<Selector> visitSelectorGroup(final SelectorGroupContext ctx) {
        List<Selector> result = new ArrayList<Selector>();
        for (SelectorContext selectorCtx : ctx.selector()) {
            result.add(visitSelector(selectorCtx));
        }
        return result;
    }

    @Override
    public Selector visitSelector(final SelectorContext ctx) {
        AbstractSelector root = buildSelector(ctx.selectorType());
        for (CombinatorContext combinatorCxt : ctx.combinator()) {
            root = visitCombinator(root, combinatorCxt);
        }
        return root;
    }

    private AbstractSelector visitCombinator(final Selector parent, final CombinatorContext ctx) {
        String scombinator = ctx.COMBINATOR.getText();
        AbstractSelector selector = buildSelector(ctx.selectorType());
        final AbstractSelector combinator;
        if (scombinator.equals(">")) {
            combinator = new ChildCombinator(parent, selector);
        } else if (scombinator.equals("+")) {
            combinator = new SiblingCombinator(parent, selector);
        } else {
            combinator = new GeneralSiblingCombinator(parent, selector);
        }
        return combinator;
    }

    private AbstractSelector buildSelector(final Iterable<SelectorTypeContext> selectorTypes) {
        int idx = -1;
        AbstractSelector root = null;
        AbstractSelector current = null;
        for (SelectorTypeContext ctx : selectorTypes) {
            if (current == null) {
                root = (AbstractSelector) visit(ctx);
                current = root;
            } else {
                AbstractSelector next = (AbstractSelector) visit(ctx);
                if (idx != -1 && idx + 1 != ctx.start.getStartIndex()) {
                    root = new DescendantCombinator(current, next);
                    current = root;
                } else {
                    current.next(next);
                    current = next;
                }
            }
            // buffer.append(ctx.getText());
            idx = ctx.stop.getStopIndex();
        }
        return root;
    }

    @Override
    public Object visitPseudo(final PseudoContext ctx) {
        String operator = ":";
        if (ctx.twoColon != null) {
            operator += ":";
        }
        if (ctx.id != null) {
            return new PseudoSelector(text(ctx.id), operator);
        }
        return new PseudoSelector(ctx.functionalPseudo().getText(), operator);
    }

    @Override
    public Object visitNegation(final NegationContext ctx) {
        Selector selector = (Selector) visit(ctx.selectorType());
        return new NotSelector(selector);
    }

    @Override
    public Object visitUniversalNamepaceUniversalSelector(final UniversalNamepaceUniversalSelectorContext ctx) {
        return new UniversalSelector("*");
    }

    @Override
    public Object visitIdentNamespaceUniversalSelector(final IdentNamespaceUniversalSelectorContext ctx) {
        return new UniversalSelector(text(ctx.prefix));
    }

    @Override
    public Object visitNonamespaceUniversalSelector(final NonamespaceUniversalSelectorContext ctx) {
        return new UniversalSelector(null);
    }

    @Override
    public Object visitUnivesalNamespaceTypeSelector(final UnivesalNamespaceTypeSelectorContext ctx) {
        return new TypeSelector("*", text(ctx.IDENT()));
    }

    @Override
    public Object visitIdentNamespaceTypeSelector(final IdentNamespaceTypeSelectorContext ctx) {
        return new TypeSelector(text(ctx.prefix), text(ctx.id));
    }

    @Override
    public Object visitNonamespaceTypeSelector(final NonamespaceTypeSelectorContext ctx) {
        return new TypeSelector(null, text(ctx.IDENT()));
    }

    @Override
    public Object visitUniversalSelector(final UniversalSelectorContext ctx) {
        return new UniversalSelector(null);
    }

    @Override
    public Object visitTypeSelector(final TypeSelectorContext ctx) {
        return new TypeSelector(null, text(ctx.IDENT()));
    }

    @Override
    public AbstractSelector visitClassSelector(final ClassSelectorContext ctx) {
        return new ClassSelector(ctx.CLASS().getText());
    }

    @Override
    public AbstractSelector visitIdSelector(final IdSelectorContext ctx) {
        return new IdSelector(text(ctx.HASH()));
    }

    @Override
    public AbstractSelector visitAttribute(final AttributeContext ctx) {
        return new AttributeSelector(text(ctx.prefix), text(ctx.name), ValueSelector.of(text(ctx.operator)),
                text(ctx.value));
    }

    private String text(final TerminalNode ctx) {
        return ctx == null ? null : ctx.getText();
    }

    private String text(final Token token) {
        return token == null ? null : token.getText();
    }
}