Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
cz.vutbr.web.csskit.antlr4.CSSParserVisitorImpl Maven / Gradle / Ivy
package cz.vutbr.web.csskit.antlr4;
import cz.vutbr.web.css.*;
import cz.vutbr.web.css.Selector.PseudoElement;
import cz.vutbr.web.csskit.RuleArrayList;
import cz.vutbr.web.csskit.antlr4.CSSParser.Bracketed_identsContext;
import cz.vutbr.web.csskit.antlr4.CSSParser.Ident_list_itemContext;
import cz.vutbr.web.csskit.antlr4.CSSParser.Keyframe_blockContext;
import cz.vutbr.web.csskit.antlr4.CSSParser.Keyframe_selectorContext;
import cz.vutbr.web.csskit.antlr4.CSSParser.Keyframes_nameContext;
import org.antlr.v4.runtime.CommonToken;
import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.RuleContext;
import org.antlr.v4.runtime.Token;
import org.antlr.v4.runtime.tree.*;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
public class CSSParserVisitorImpl implements CSSParserVisitor, CSSParserExtractor {
// factories for building structures
private RuleFactory rf = CSSFactory.getRuleFactory();
private TermFactory tf = CSSFactory.getTermFactory();
private enum MediaQueryState {START, TYPE, AND, EXPR, TYPEOREXPR}
//logger
private org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(getClass());
//counter of spaces for pretty debug printing
private int spacesCounter = 0;
// block preparator
private Preparator preparator;
// list of media queries to wrap rules
private List wrapMedia;
// structures after parsing
private List importPaths = new ArrayList<>();
private List> importMedia = new ArrayList<>();
private RuleList rules = null;
private List mediaQueryList = null;
//prevent imports inside the style sheet
private boolean preventImports = false;
private void logEnter(String entry) {
if (log.isTraceEnabled())
log.trace("Enter: {}{}", generateSpaces(spacesCounter), entry);
}
private void logEnter(String entry, RuleContext ctx) {
if (log.isTraceEnabled())
log.trace("Enter: {}{}: >{}<", generateSpaces(spacesCounter), entry, ctx.getText());
}
private void logLeave(String leaving) {
if (log.isTraceEnabled())
log.trace("Leave: {}{}", generateSpaces(spacesCounter), leaving);
}
private String extractTextUnescaped(String text) {
return org.unbescape.css.CssEscape.unescapeCss(text);
}
private Declaration.Source extractSource(CSSToken ct) {
return new Declaration.Source(ct.getBase(), ct.getLine(), ct.getCharPositionInLine());
}
/**
* extract base from parse tree node
*/
private URL extractBase(TerminalNode node) {
CSSToken ct = (CSSToken) node.getSymbol();
return ct.getBase();
}
/**
* check if string is valid ID
*
* @param id ID to validate and unescapes
* @return unescaped id or null
*/
private String extractIdUnescaped(String id) {
if (!id.isEmpty() && !Character.isDigit(id.charAt(0))) {
return org.unbescape.css.CssEscape.unescapeCss(id);
}
return null;
}
/**
* generate spaces for pretty debug printing
*
* @param count number of generated spaces
* @return string with spaces
*/
private String generateSpaces(int count) {
String spaces = "";
for (int i = 0; i < count; i++) {
spaces += " ";
}
return spaces;
}
/**
* remove terminal node emtpy tokens from input list
*
* @param inputArrayList original list
* @return list without terminal node type = S (space)
*/
private List filterSpaceTokens(List inputArrayList) {
List ret = new ArrayList(inputArrayList.size());
for (ParseTree item : inputArrayList) {
if (!(item instanceof TerminalNode) || ((TerminalNodeImpl) item).getSymbol().getType() != CSSLexer.S) {
ret.add(item);
}
}
return ret;
}
/**
* check if rule context contains error node
*
* @param ctx rule context
* @return contains context error node
*/
private boolean ctxHasErrorNode(ParserRuleContext ctx) {
for (int i = 0; i < ctx.children.size(); i++) {
if (ctx.getChild(i) instanceof ErrorNode) {
return true;
}
}
return false;
}
/**
* Tries to convert generic terms to more specific value types. Currently, colors (TermColor) and
* rectangles (TermRect) are supported.
* @param term the term to be converted
* @return the corresponding more specific term type or {@code null} when nothing was found.
*/
private Term> findSpecificType(Term> term)
{
TermColor colorTerm = null;
TermRect rectTerm = null;
if (term instanceof TermIdent) { //idents - try to convert colors
colorTerm = tf.createColor((TermIdent) term);
} else if (term instanceof TermFunction) { // rgba(0,0,0)
colorTerm = tf.createColor((TermFunction) term);
if (colorTerm == null)
rectTerm = tf.createRect((TermFunction) term);
}
//replace with more specific value
if (colorTerm != null) {
if (log.isDebugEnabled()) {
log.debug("term color is OK - creating - " + colorTerm.toString());
}
return colorTerm;
} else if (rectTerm != null) {
if (log.isDebugEnabled()) {
log.debug("term rect is OK - creating - " + rectTerm.toString());
}
return rectTerm;
} else
return null;
}
/**
* get parsed rulelist
*
* @return parsed rules
*/
public RuleList getRules() {
return rules;
}
/**
* get mediaquery list
*
* @return media query list
*/
public List getMedia() {
return mediaQueryList;
}
/**
* get import list
*
* @return list of urls to import
*/
public List getImportPaths() {
return importPaths;
}
/**
* get media for imports
*
* @return list of media for imports
*/
public List> getImportMedia() {
return importMedia;
}
/**
* Constructor
*
* @param preparator The preparator to be used for creating the rules.
* @param wrapMedia The media queries to be used for wrapping the created rules (e.g. in case
* of parsing and imported style sheet) or null when no wrapping is required.
*/
public CSSParserVisitorImpl(Preparator preparator, List wrapMedia) {
this.preparator = preparator;
this.wrapMedia = wrapMedia;
}
//used in parseMediaQuery
public CSSParserVisitorImpl() {
}
/******************************************************************
/******************************************************************
/******************************************************************
/******************************************************************
/******************************************************************
/******************************************************************
/******************************************************************
/******************************************************************
/******************************************************************/
/**
* @param ctx the parse tree
* @return RuleList
* inlinestyle: S* (declarations | inlineset+ )
*/
@Override
public RuleList visitInlinestyle(CSSParser.InlinestyleContext ctx) {
logEnter("inlinestyle");
this.rules = new cz.vutbr.web.csskit.RuleArrayList();
if (ctx.declarations() != null) {
//declarations
List decl = visitDeclarations(ctx.declarations());
cz.vutbr.web.css.RuleBlock> rb = preparator.prepareInlineRuleSet(decl, null);
if (rb != null) {
//rb is valid,add to rules
this.rules.add(rb);
}
} else {
//inlineset
for (CSSParser.InlinesetContext ctxis : ctx.inlineset()) {
cz.vutbr.web.css.RuleBlock> irs = visitInlineset(ctxis);
if (irs != null) {
//irs is valid, add to rules
this.rules.add(irs);
}
}
}
if (log.isDebugEnabled()) {
log.debug("\n***\n{}\n***\n", this.rules);
}
logLeave("inlinestyle");
return this.rules;
}
/**
* Stylesheet, main rule
* stylesheet: ( CDO | CDC | S | nostatement | statement )*
* statement* is only processed
*/
@Override
public RuleList visitStylesheet(CSSParser.StylesheetContext ctx) {
logEnter("stylesheet: ", ctx);
this.rules = new RuleArrayList();
//statement*
for (CSSParser.StatementContext stmt : ctx.statement()) {
RuleBlock> s = visitStatement(stmt);
if (s != null) {
//add statement to rules
this.rules.add(s);
}
}
if (log.isDebugEnabled()) {
log.debug("\n***\n{}\n***\n", this.rules);
}
logLeave("stylesheet");
return this.rules;
}
/**
* scope and stack for statement
* - this is for accessing statement scope from inner rules
* e.g. - used for invalidate statement from selector
*/
protected static class statement_scope {
boolean invalid = false;
}
/**
* stack for posibly recursion
*/
protected Stack statement_stack = new Stack<>();
@Override
/**
* Statement, main contents unit
* statement : ruleset | atstatement
*/
public RuleBlock> visitStatement(CSSParser.StatementContext ctx) {
if (ctxHasErrorNode(ctx)) {
//context is invalid
return null;
}
logEnter("statement: ", ctx);
//create new scope and push it to stack
statement_stack.push(new statement_scope());
RuleBlock> stmt = null;
if (ctx.ruleset() != null) {
//ruleset
stmt = visitRuleset(ctx.ruleset());
} else if (ctx.atstatement() != null) {
//atstatement
stmt = visitAtstatement(ctx.atstatement());
}
if (statement_stack.peek().invalid) {
//stmt == null - is invalid
if (log.isDebugEnabled()) {
log.debug("Statement is invalid");
}
}
statement_stack.pop();
logLeave("statement");
//could be null
return stmt;
}
@Override
/**
*
atstatement
: CHARSET
| IMPORT S* import_uri S* media? SEMICOLON
| page
| VIEWPORT S* LCURLY S* declarations RCURLY
| FONTFACE S* LCURLY S* declarations RCURLY
| MEDIA S* media? LCURLY S* (media_rule S*)* RCURLY
| unknown_atrule
;
*/
public RuleBlock> visitAtstatement(CSSParser.AtstatementContext ctx) {
logEnter("atstatement: ", ctx);
RuleBlock> atstmt = null;
//noinspection StatementWithEmptyBody
if (ctx.CHARSET() != null) {
//charset is served in lexer
}
//import
else if (ctx.IMPORT() != null) {
List im = null;
if (ctx.media() != null) {
im = visitMedia(ctx.media());
}
ctx.import_uri();
String iuri = visitImport_uri(ctx.import_uri());
if (!this.preventImports && iuri != null) {
if (log.isDebugEnabled()) {
log.debug("Adding import: {}", iuri);
}
importMedia.add(im);
importPaths.add(iuri);
} else {
if (log.isDebugEnabled()) {
log.debug("Ignoring import: {}", iuri);
}
}
}
//page
else if (ctx.page() != null) {
atstmt = visitPage(ctx.page());
}
//viewport
else if (ctx.VIEWPORT() != null) {
List declarations = visitDeclarations(ctx.declarations());
atstmt = preparator.prepareRuleViewport(declarations);
if (atstmt != null)
this.preventImports = true;
}
//fontface
else if (ctx.FONTFACE() != null) {
List declarations = visitDeclarations(ctx.declarations());
atstmt = preparator.prepareRuleFontFace(declarations);
if (atstmt != null)
this.preventImports = true;
}
//media
else if (ctx.MEDIA() != null) {
List mediaList = null;
List rules = null;
if (ctx.media() != null) {
mediaList = visitMedia(ctx.media());
}
if (ctx.media_rule() != null) {
rules = new ArrayList<>();
for (CSSParser.Media_ruleContext mr : ctx.media_rule()) {
RuleBlock> rs = visitMedia_rule(mr);
if (rs != null) {
rules.add((RuleSet) rs);
}
}
}
atstmt = preparator.prepareRuleMedia(rules, mediaList);
if (atstmt != null)
this.preventImports = true;
}
//keyframes
else if (ctx.KEYFRAMES() != null) {
String name = null;
List keyframes = null;
if (ctx.keyframes_name() != null) {
name = visitKeyframes_name(ctx.keyframes_name());
}
if (ctx.keyframe_block() != null) {
keyframes = new ArrayList<>();
for (CSSParser.Keyframe_blockContext kfctx : ctx.keyframe_block()) {
KeyframeBlock block = visitKeyframe_block(kfctx);
if (block != null) {
keyframes.add(block);
}
}
}
atstmt = preparator.prepareRuleKeyframes(keyframes, name);
if (atstmt != null)
this.preventImports = true;
}
//unknown
else {
if (log.isDebugEnabled()) {
log.debug("Skipping invalid at statement");
}
}
logLeave("atstatement");
return atstmt;
}
@Override
/**
* import_uri : (STRING | URI)
*/
public String visitImport_uri(CSSParser.Import_uriContext ctx) {
if (ctx != null)
return extractTextUnescaped(ctx.getText());
else
return null;
}
@Override
/**
*
page
: PAGE S* IDENT? pseudo? S*
LCURLY S*
declarations margin_rule*
RCURLY
*/
public RuleBlock> visitPage(CSSParser.PageContext ctx) {
boolean invalid = false;
String name = null;
if (ctx.IDENT() != null) {
name = extractTextUnescaped(ctx.IDENT().getText());
}
Selector.PseudoPage pseudo = null;
if (ctx.pseudo() != null) {
Selector.SelectorPart p = visitPseudo(ctx.pseudo());
if (p != null && p instanceof Selector.PseudoPage) {
pseudo = (Selector.PseudoPage) p;
} else { // Invalid pseudo
if (log.isDebugEnabled()) {
log.debug("skipping RulePage with invalid pseudo-class: " + pseudo);
}
invalid = true;
}
}
List declarations = visitDeclarations(ctx.declarations());
List margins = null;
if (ctx.margin_rule() != null) {
margins = new ArrayList<>();
for (CSSParser.Margin_ruleContext mctx : ctx.margin_rule()) {
RuleMargin m = visitMargin_rule(mctx);
margins.add(m);
if (log.isDebugEnabled()) {
log.debug("Inserted margin rule #{} into @page", margins.size() + 1);
}
}
}
if (invalid) {
return null;
} else {
RuleBlock> rb = preparator.prepareRulePage(declarations, margins, name, pseudo);
if (rb != null)
this.preventImports = true;
return rb;
}
}
@Override
/**
* margin_rule : MARGIN_AREA S* LCURLY S* declarations RCURLY S*
*/
public RuleMargin visitMargin_rule(CSSParser.Margin_ruleContext ctx) {
logEnter("margin_rule");
RuleMargin m;
String area = ctx.MARGIN_AREA().getText();
List decl = visitDeclarations(ctx.declarations());
m = preparator.prepareRuleMargin(extractTextUnescaped(area).substring(1), decl);
logLeave("margin_rule");
return m;
}
@Override
/**
*
inlineset
: (pseudo S* (COMMA S* pseudo S*)*)?
LCURLY
declarations
RCURLY
;
*/
public cz.vutbr.web.css.RuleBlock> visitInlineset(CSSParser.InlinesetContext ctx) {
logEnter("inlineset");
List pplist = new ArrayList<>();
if (ctx.pseudo() != null) {
for (CSSParser.PseudoContext pctx : ctx.pseudo()) {
Selector.SelectorPart p = visitPseudo(pctx);
pplist.add(p);
}
}
List decl = visitDeclarations(ctx.declarations());
RuleBlock> is = preparator.prepareInlineRuleSet(decl, pplist);
logLeave("inlineset");
return is;
}
@Override
/**
media : media_query (COMMA S* media_query)*
*/
public List visitMedia(CSSParser.MediaContext ctx) {
logEnter("media: ", ctx);
List queries = mediaQueryList = new ArrayList<>();
for (CSSParser.Media_queryContext mqc : ctx.media_query()) {
queries.add(visitMedia_query(mqc));
}
if (log.isDebugEnabled()) {
log.debug("Totally returned {} media queries.", queries.size());
}
logLeave("media");
return queries;
}
protected static class mediaquery_scope {
cz.vutbr.web.css.MediaQuery q;
MediaQueryState state;
boolean invalid;
}
mediaquery_scope mq;
@Override
/**
* media_query : (media_term S*)+
*/
public MediaQuery visitMedia_query(CSSParser.Media_queryContext ctx) {
logEnter("mediaquery: ", ctx);
mq = new mediaquery_scope();
mq.q = rf.createMediaQuery();
mq.q.unlock();
mq.state = MediaQueryState.START;
mq.invalid = false;
logLeave("mediaquery");
for (CSSParser.Media_termContext mtc : ctx.media_term()) {
visitMedia_term(mtc);
}
if (mq.invalid) {
log.trace("Skipping invalid rule {}", mq.q);
mq.q.setType("all"); //change the malformed media queries to "not all"
mq.q.setNegative(true);
}
logLeave("mediaquery");
return mq.q;
}
@Override
/**
*
media_term
: (IDENT | media_expression)
| nomediaquery
*/
public Object visitMedia_term(CSSParser.Media_termContext ctx) {
//IDENT
if (ctx.IDENT() != null) {
String m = extractTextUnescaped(ctx.IDENT().getText());
MediaQueryState state = mq.state;
if (m.equalsIgnoreCase("ONLY") && state == MediaQueryState.START) {
mq.state = MediaQueryState.TYPEOREXPR;
} else if (m.equalsIgnoreCase("NOT") && state == MediaQueryState.START) {
mq.q.setNegative(true);
mq.state = MediaQueryState.TYPEOREXPR;
} else if (m.equalsIgnoreCase("AND") && state == MediaQueryState.AND) {
mq.state = MediaQueryState.EXPR;
} else if (state == MediaQueryState.START
|| state == MediaQueryState.TYPE
|| state == MediaQueryState.TYPEOREXPR) {
mq.q.setType(m);
mq.state = MediaQueryState.AND;
} else {
log.trace("Invalid media query: found ident: {} state: {}", m, state);
mq.invalid = true;
}
}
//media_expression
else if (ctx.media_expression() != null) {
MediaExpression e = visitMedia_expression(ctx.media_expression());
if (mq.state == MediaQueryState.START
|| mq.state == MediaQueryState.EXPR
|| mq.state == MediaQueryState.TYPEOREXPR) {
if (e != null && e.getFeature() != null) //the expression is valid
{
mq.q.add(e);
mq.state = MediaQueryState.AND;
} else {
log.trace("Invalidating media query for invalud expression");
mq.invalid = true;
}
} else {
log.trace("Invalid media query: found expr, state: {}", mq.state);
mq.invalid = true;
}
}
//nomediaquery
else {
mq.invalid = true;
}
return null;
}
@Override
/**
* media_expression : LPAREN S* IDENT S* (COLON S* terms)? RPAREN
*/
public MediaExpression visitMedia_expression(CSSParser.Media_expressionContext ctx) {
logEnter("mediaexpression: ", ctx);
if (ctxHasErrorNode(ctx)) {
mq.invalid = true;
return null;
}
MediaExpression expr = rf.createMediaExpression();
Declaration decl;
declaration_stack.push(new declaration_scope());
declaration_stack.peek().d = decl = rf.createDeclaration();
declaration_stack.peek().invalid = false;
String property = extractTextUnescaped(ctx.IDENT().getText());
decl.setProperty(property);
Token token = ctx.IDENT().getSymbol();
decl.setSource(extractSource((CSSToken) token));
if (ctx.terms() != null) {
List> t = visitTerms(ctx.terms());
decl.replaceAll(t);
}
if (declaration_stack.peek().d != null && !declaration_stack.peek().invalid) { //if the declaration is valid
expr.setFeature(decl.getProperty());
expr.replaceAll(decl);
}
declaration_stack.pop();
logLeave("mediaexpression");
return expr;
}
@Override
/**
*
media_rule
: ruleset
| atstatement //invalid statement
;
*/
public RuleBlock> visitMedia_rule(CSSParser.Media_ruleContext ctx) {
logEnter("media_rule: ", ctx);
RuleBlock> rules = null;
if (ctx.ruleset() != null) {
statement_stack.push(new statement_scope());
rules = visitRuleset(ctx.ruleset());
statement_stack.pop();
} else {
if (log.isDebugEnabled()) {
log.debug("Skiping invalid statement in media");
}
}
logLeave("media_rule");
//could be null
return rules;
}
@Override
public String visitKeyframes_name(Keyframes_nameContext ctx) {
if (ctx.IDENT() != null)
return extractTextUnescaped(ctx.IDENT().getText());
else if (ctx.string() != null)
return visitString(ctx.string());
else
return null;
}
@Override
public KeyframeBlock visitKeyframe_block(Keyframe_blockContext ctx) {
List selectors = null;
if (ctx.keyframe_selector() != null) {
selectors = new ArrayList<>();
for (Keyframe_selectorContext selctx : ctx.keyframe_selector()) {
TermPercent perc = visitKeyframe_selector(selctx);
if (perc != null)
selectors.add(perc);
}
}
List declarations = null;
if (ctx.declarations() != null) {
statement_stack.push(new statement_scope());
declarations = visitDeclarations(ctx.declarations());
statement_stack.pop();
}
if (declarations != null && selectors != null && !selectors.isEmpty()) {
KeyframeBlock block = rf.createKeyframeBlock();
block.setPercentages(selectors);
block.replaceAll(declarations);
return block;
} else {
return null;
}
}
@Override
public TermPercent visitKeyframe_selector(Keyframe_selectorContext ctx) {
if (ctx.IDENT() != null) {
final String idtext = ctx.IDENT().getText();
if (idtext != null) {
if (idtext.equalsIgnoreCase("from")) {
return tf.createPercent(0.0f);
} else if (idtext.equalsIgnoreCase("to")) {
return tf.createPercent(100.0f);
} else {
return null;
}
} else {
return null;
}
} else if (ctx.PERCENTAGE() != null) {
return tf.createPercent(ctx.PERCENTAGE().getText(), 1);
} else {
return null;
}
}
@Override
public Object visitUnknown_atrule(CSSParser.Unknown_atruleContext ctx) {
//done in atstatement else section
return null;
}
@Override
public Object visitUnknown_atrule_body(CSSParser.Unknown_atrule_bodyContext ctx) {
//not used - the unknown atrules are skipped
return null;
}
@Override
/**
* The most common block in CSS file,
* set of declarations with selector
ruleset
: combined_selector (COMMA S* combined_selector)*
LCURLY S*
declarations
RCURLY
| norule
*/
public RuleBlock> visitRuleset(CSSParser.RulesetContext ctx) {
logEnter("ruleset");
if (ctxHasErrorNode(ctx) || ctx.norule() != null) {
log.trace("Leaving ruleset with error {} {}", ctxHasErrorNode(ctx), (ctx.norule() != null));
return null;
}
List cslist = new ArrayList<>();
// body
for (CSSParser.Combined_selectorContext csctx : ctx.combined_selector()) {
CombinedSelector cs = visitCombined_selector(csctx);
if (cs != null && !cs.isEmpty() && !statement_stack.peek().invalid) {
cslist.add(cs);
if (log.isDebugEnabled()) {
log.debug("Inserted combined selector ({}) into ruleset", cslist.size());
}
}
}
List decl = visitDeclarations(ctx.declarations());
RuleBlock> stmnt;
if (statement_stack.peek().invalid) {
stmnt = null;
if (log.isDebugEnabled()) {
log.debug("Ruleset not valid, so not created");
}
} else {
stmnt = preparator.prepareRuleSet(cslist, decl, (this.wrapMedia != null && !this.wrapMedia.isEmpty()), this.wrapMedia);
this.preventImports = true;
}
logLeave("ruleset");
return stmnt;
}
@Override
/**
* Multiple CSS declarations
* declarations : declaration? (SEMICOLON S* declaration? )*
*/
public List visitDeclarations(CSSParser.DeclarationsContext ctx) {
logEnter("declarations");
List decl = new ArrayList<>();
if (ctx != null && ctx.declaration() != null) {
for (CSSParser.DeclarationContext declctx : ctx.declaration()) {
Declaration d = visitDeclaration(declctx);
if (d != null) {
decl.add(d);
if (log.isDebugEnabled()) {
log.debug("Inserted declaration #{} ", decl.size() + 1);
}
} else {
if (log.isDebugEnabled()) {
log.debug("Null declaration was omitted");
}
}
}
}
logLeave("declarations");
return decl;
}
protected static class declaration_scope {
cz.vutbr.web.css.Declaration d;
boolean invalid;
}
protected Stack declaration_stack = new Stack<>();
@Override
/**
*
declaration : property COLON S* terms? important?
| noprop any*
*/
public Declaration visitDeclaration(CSSParser.DeclarationContext ctx) {
logEnter("declaration");
Declaration decl;
declaration_stack.push(new declaration_scope());
declaration_stack.peek().d = decl = rf.createDeclaration();
declaration_stack.peek().invalid = false;
if (ctx.noprop() == null && !ctxHasErrorNode(ctx)) {
if (ctx.important() != null) {
visitImportant(ctx.important());
}
visitProperty(ctx.property());
if (ctx.terms() != null) {
List> t = visitTerms(ctx.terms());
decl.replaceAll(t);
}
} else {
if (log.isDebugEnabled()) {
log.debug("invalidating declaration");
}
declaration_stack.peek().invalid = true;
}
if (declaration_stack.peek().invalid || declaration_stack.isEmpty()) {
decl = null;
if (log.isDebugEnabled()) {
log.debug("Declaration was invalidated or already invalid");
}
} else {
if (log.isDebugEnabled()) {
log.debug("Returning declaration: {}.", decl);
}
}
logLeave("declaration");
declaration_stack.pop();
return decl;
}
@Override
/**
* important
: EXCLAMATION S* IMPORTANT S*
*/
public Object visitImportant(CSSParser.ImportantContext ctx) {
if (ctxHasErrorNode(ctx)) {
declaration_stack.peek().invalid = true;
} else {
declaration_stack.peek().d.setImportant(true);
if (log.isDebugEnabled()) {
log.debug("IMPORTANT");
}
}
//returns null
return null;
}
@Override
/**
* Setting property of declaration
property
: MINUS? IDENT S*
returns null - processed via declaration_sctack
*/
public Object visitProperty(CSSParser.PropertyContext ctx) {
logEnter("property");
String property = extractTextUnescaped(ctx.IDENT().getText());
if (ctx.MINUS() != null) {
property = ctx.MINUS().getText() + property;
}
declaration_stack.peek().d.setProperty(property);
Token token = ctx.IDENT().getSymbol();
declaration_stack.peek().d.setSource(extractSource((CSSToken) token));
if (log.isDebugEnabled()) {
log.debug("Setting property: {}", declaration_stack.peek().d.getProperty());
}
logLeave("property");
//returns null
return null;
}
protected static class terms_scope {
List> list;
cz.vutbr.web.css.Term> term;
cz.vutbr.web.css.Term.Operator op;
int unary;
boolean dash;
}
protected Stack terms_stack = new Stack<>();
@Override
/**
* Term of CSSDeclaration
*
* terms : term+
*/
public List> visitTerms(CSSParser.TermsContext ctx) {
terms_stack.push(new terms_scope());
List> tlist;
logEnter("terms");
terms_stack.peek().list = tlist = new ArrayList<>();
terms_stack.peek().term = null;
terms_stack.peek().op = null;
terms_stack.peek().unary = 1;
terms_stack.peek().dash = false;
if (ctx.term() != null)
{
for (CSSParser.TermContext trmCtx : ctx.term()) {
if (trmCtx instanceof CSSParser.TermValuePartContext) {
visitTermValuePart((CSSParser.TermValuePartContext) trmCtx);
// set operator, store and create next
if (!declaration_stack.peek().invalid && terms_stack.peek().term != null) {
terms_stack.peek().term.setOperator(terms_stack.peek().op);
terms_stack.peek().list.add(terms_stack.peek().term);
// reinitialization
terms_stack.peek().op = cz.vutbr.web.css.Term.Operator.SPACE;
terms_stack.peek().unary = 1;
terms_stack.peek().dash = false;
terms_stack.peek().term = null;
}
} else {
visitTermInvalid((CSSParser.TermInvalidContext) trmCtx);
}
}
}
if (log.isDebugEnabled()) {
log.debug("Totally added {} terms", tlist.size());
}
logLeave("terms");
terms_stack.pop();
return tlist;
}
/**
* term
* : valuepart #termValuePart
* | LCURLY S* (any | SEMICOLON S*)* RCURLY #termInvalid // invalid term
* | ATKEYWORD S* #termInvalid // invalid term
* ;
*/
//
@Override
public Object visitTermValuePart(CSSParser.TermValuePartContext ctx) {
logEnter("term");
visitValuepart(ctx.valuepart());
//returns null
return null;
}
@Override
public Object visitTermInvalid(CSSParser.TermInvalidContext ctx) {
logEnter("term");
declaration_stack.peek().invalid = true;
//returns null
return null;
}
@Override
public Object visitFunct(CSSParser.FunctContext ctx) {
if (ctx.EXPRESSION() != null) {
log.warn("Omitting expression " + ctx.getText() + ", expressions are not supported");
return null;
}
Term> ret = null;
final String fname = extractTextUnescaped(ctx.FUNCTION().getText()).toLowerCase();
if (ctx.funct_args() != null)
{
List> t = visitFunct_args(ctx.funct_args());
if (fname.equals("url")) {
// the function name is url() after escaping - create an URI
if (t == null || t.size() != 1)
ret = null;
else {
cz.vutbr.web.css.Term> term = t.get(0);
if (term instanceof cz.vutbr.web.css.TermString && term.getOperator() == null)
ret = tf.createURI(((cz.vutbr.web.css.TermString) term).getValue(), extractBase(ctx.FUNCTION()));
else
ret = null;
}
} else if (fname.equals("calc")) {
// create calc() of the given type: , , , , , or
if (t == null || t.size() == 0)
ret = null;
else
ret = tf.createCalc(t);
} else {
// create function
ret = tf.createFunction(fname, t);
}
}
return ret;
}
@Override
public Object visitValuepart(CSSParser.ValuepartContext ctx) {
logEnter("valuepart: ", ctx);
if (ctxHasErrorNode(ctx)) {
log.error("value part with error");
terms_stack.peek().term = null;
declaration_stack.peek().invalid = true;
return null;
}
if (ctx.MINUS() != null) {
terms_stack.peek().unary = -1;
terms_stack.peek().dash = true;
}
if (ctx.COMMA() != null) {
if (log.isDebugEnabled()) {
log.debug("VP - comma");
}
terms_stack.peek().op = Term.Operator.COMMA;
} else if (ctx.SLASH() != null) {
terms_stack.peek().op = Term.Operator.SLASH;
} else if (ctx.string() != null) {
//string
if (log.isDebugEnabled()) {
log.debug("VP - string");
}
terms_stack.peek().term = tf.createString(extractTextUnescaped(ctx.string().getText()));
} else if (ctx.IDENT() != null) {
if (log.isDebugEnabled()) {
log.debug("VP - ident");
}
terms_stack.peek().term = tf.createIdent(extractTextUnescaped(ctx.IDENT().getText()), terms_stack.peek().dash);
} else if (ctx.HASH() != null) {
if (log.isDebugEnabled()) {
log.debug("VP - hash");
}
terms_stack.peek().term = tf.createColor(ctx.HASH().getText());
if (terms_stack.peek().term == null) {
declaration_stack.peek().invalid = true;
}
} else if (ctx.PERCENTAGE() != null) {
if (log.isDebugEnabled()) {
log.debug("VP - percentage");
}
terms_stack.peek().term = tf.createPercent(ctx.PERCENTAGE().getText(), terms_stack.peek().unary);
} else if (ctx.DIMENSION() != null) {
if (log.isDebugEnabled()) {
log.debug("VP - dimension");
}
String dim = ctx.DIMENSION().getText();
terms_stack.peek().term = tf.createDimension(dim, terms_stack.peek().unary);
if (terms_stack.peek().term == null) {
log.info("Unable to create dimension from {}, unary {}", dim, terms_stack.peek().unary);
declaration_stack.peek().invalid = true;
}
} else if (ctx.NUMBER() != null) {
if (log.isDebugEnabled()) {
log.debug("VP - number");
}
terms_stack.peek().term = tf.createNumeric(ctx.NUMBER().getText(), terms_stack.peek().unary);
} else if (ctx.UNIRANGE() != null) {
if (log.isDebugEnabled()) {
log.debug("VP - unirange");
}
terms_stack.peek().term = tf.createUnicodeRange(ctx.UNIRANGE().getText());
} else if (ctx.URI() != null) {
if (log.isDebugEnabled()) {
log.debug("VP - uri");
}
terms_stack.peek().term = tf.createURI(extractTextUnescaped(ctx.URI().getText()), extractBase(ctx.URI()));
} else if (ctx.UNCLOSED_URI() != null && ((CSSToken) ctx.UNCLOSED_URI().getSymbol()).isValid()) {
if (log.isDebugEnabled()) {
log.debug("VP - unclosed_uri");
}
terms_stack.peek().term = tf.createURI(extractTextUnescaped(ctx.UNCLOSED_URI().getText()), extractBase(ctx.UNCLOSED_URI()));
} else if (ctx.funct() != null) {
terms_stack.peek().term = null;
Term> fnterm = (Term>) visitFunct(ctx.funct());
if (fnterm != null) {
if (terms_stack.peek().unary == -1) {
if (fnterm instanceof TermFunction) {
//normal function - add the unary minus to the function name
((TermFunction) fnterm).setFunctionName('-' + ((TermFunction) fnterm).getFunctionName());
terms_stack.peek().term = fnterm;
} else {
//url() and calc() - not applicable
declaration_stack.peek().invalid = true;
}
} else {
terms_stack.peek().term = fnterm;
}
} else {
declaration_stack.peek().invalid = true;
}
} else if (ctx.bracketed_idents() != null) {
if (log.isDebugEnabled()) {
log.debug("VP - bracketed_idents");
}
terms_stack.peek().term = (TermBracketedIdents) visitBracketed_idents(ctx.bracketed_idents());
if (terms_stack.peek().term == null)
declaration_stack.peek().invalid = true; //invalid bracketed ident - invalidate the whole declaration
} else {
log.error("unhandled valueparts");
terms_stack.peek().term = null;
declaration_stack.peek().invalid = true;
}
//try to convert generic terms to more specific value types
Term> term = terms_stack.peek().term;
if (term != null) {
term = findSpecificType(term);
if (term != null)
terms_stack.peek().term = term;
}
//returns null
return null;
}
protected static class funct_args_scope {
List> list;
cz.vutbr.web.css.Term> term;
}
protected Stack funct_args_stack = new Stack<>();
@Override
public List> visitFunct_args(CSSParser.Funct_argsContext ctx)
{
funct_args_stack.push(new funct_args_scope());
List> tlist;
funct_args_stack.peek().term = null;
logEnter("funct_args");
funct_args_stack.peek().list = tlist = new ArrayList<>();
if (ctx.funct_argument() != null)
{
for (CSSParser.Funct_argumentContext argCtx : ctx.funct_argument()) {
visitFunct_argument(argCtx);
// set operator, store and create next
if (!declaration_stack.peek().invalid && funct_args_stack.peek().term != null) {
funct_args_stack.peek().list.add(funct_args_stack.peek().term);
funct_args_stack.peek().term = null;
}
}
}
if (log.isDebugEnabled()) {
log.debug("Totally added {} args", tlist.size());
}
logLeave("funct_args");
funct_args_stack.pop();
return tlist;
}
@Override
public Object visitFunct_argument(CSSParser.Funct_argumentContext ctx)
{
logEnter("funct_argument: ", ctx);
if (ctxHasErrorNode(ctx)) {
log.error("argument with error");
funct_args_stack.peek().term = null;
declaration_stack.peek().invalid = true;
return null;
}
if (ctx.PLUS() != null) {
if (log.isDebugEnabled()) {
log.debug("FA - plus");
}
funct_args_stack.peek().term = tf.createOperator('+');
} else if (ctx.MINUS() != null) {
if (log.isDebugEnabled()) {
log.debug("FA - minus");
}
funct_args_stack.peek().term = tf.createOperator('-');
} else if (ctx.ASTERISK() != null) {
if (log.isDebugEnabled()) {
log.debug("FA - *");
}
funct_args_stack.peek().term = tf.createOperator('*');
} else if (ctx.SLASH() != null) {
if (log.isDebugEnabled()) {
log.debug("FA - /");
}
funct_args_stack.peek().term = tf.createOperator('/');
} else if (ctx.LPAREN() != null) {
if (log.isDebugEnabled()) {
log.debug("FA - (");
}
funct_args_stack.peek().term = tf.createOperator('(');
} else if (ctx.RPAREN() != null) {
if (log.isDebugEnabled()) {
log.debug("FA - )");
}
funct_args_stack.peek().term = tf.createOperator(')');
} else if (ctx.COMMA() != null) {
if (log.isDebugEnabled()) {
log.debug("FA - comma");
}
funct_args_stack.peek().term = tf.createOperator(',');
} else if (ctx.string() != null) {
if (log.isDebugEnabled()) {
log.debug("FA - string");
}
funct_args_stack.peek().term = tf.createString(extractTextUnescaped(ctx.string().getText()));
} else if (ctx.IDENT() != null) {
if (log.isDebugEnabled()) {
log.debug("FA - ident");
}
funct_args_stack.peek().term = tf.createIdent(extractTextUnescaped(ctx.IDENT().getText()));
} else if (ctx.PERCENTAGE() != null) {
if (log.isDebugEnabled()) {
log.debug("FA - percentage");
}
funct_args_stack.peek().term = tf.createPercent(ctx.PERCENTAGE().getText(), 1);
} else if (ctx.DIMENSION() != null) {
if (log.isDebugEnabled()) {
log.debug("FA - dimension");
}
String dim = ctx.DIMENSION().getText();
funct_args_stack.peek().term = tf.createDimension(dim, 1);
if (funct_args_stack.peek().term == null) {
log.info("Unable to create dimension from {}, unary {}", dim, 1);
declaration_stack.peek().invalid = true;
}
} else if (ctx.HASH() != null) {
if (log.isDebugEnabled()) {
log.debug("FA - hash");
}
funct_args_stack.peek().term = tf.createColor(ctx.HASH().getText());
if (funct_args_stack.peek().term == null) {
declaration_stack.peek().invalid = true;
}
} else if (ctx.NUMBER() != null) {
if (log.isDebugEnabled()) {
log.debug("FA - number");
}
funct_args_stack.peek().term = tf.createNumeric(ctx.NUMBER().getText(), 1);
} else if (ctx.funct() != null) {
if (log.isDebugEnabled()) {
log.debug("FA - funct");
}
funct_args_stack.peek().term = null;
Term> fnterm = (Term>) visitFunct(ctx.funct());
if (fnterm != null) {
funct_args_stack.peek().term = fnterm;
} else {
declaration_stack.peek().invalid = true;
}
} else {
log.error("unhandled funct_args");
funct_args_stack.peek().term = null;
declaration_stack.peek().invalid = true;
}
//try convert color from current term
Term> term = funct_args_stack.peek().term;
if (term != null) {
term = findSpecificType(term);
if (term != null)
funct_args_stack.peek().term = term;
}
//returns null
return null;
}
@Override
public Object visitBracketed_idents(Bracketed_identsContext ctx) {
if (ctx.INVALID_STATEMENT() != null) {
if (log.isDebugEnabled()) {
log.debug("VP - ident invalid");
}
return null;
}
TermBracketedIdents ret = tf.createBracketedIdents();
if (ctx.ident_list_item() != null) {
for (Ident_list_itemContext ictx : ctx.ident_list_item()) {
TermIdent t = (TermIdent) visitIdent_list_item(ictx);
if (t != null)
ret.add(t);
else
return null;
}
}
return ret;
}
@Override
public Object visitIdent_list_item(Ident_list_itemContext ctx) {
boolean dash = false;
if (ctx.INVALID_STATEMENT() != null) {
if (log.isDebugEnabled()) {
log.debug("VP - ident invalid");
}
return null;
}
if (ctx.MINUS() != null) {
dash = true;
}
if (ctx.IDENT() != null) {
if (log.isDebugEnabled()) {
log.debug("VP - ident item");
}
return tf.createIdent(extractTextUnescaped(ctx.IDENT().getText()), dash);
}
else
return null;
}
protected static class combined_selector_scope {
boolean invalid;
}
protected Stack combined_selector_stack = new Stack<>();
@Override
/**
* Construction of selector
* combined_selector : selector ((combinator) selector)*
*/
public CombinedSelector visitCombined_selector(CSSParser.Combined_selectorContext ctx) {
logEnter("combined_selector");
combined_selector_stack.push(new combined_selector_scope());
CombinedSelector combinedSelector = (CombinedSelector) rf.createCombinedSelector().unlock();
Selector.Combinator c;
Selector s = visitSelector(ctx.selector(0));
combinedSelector.add(s);
for (int i = 1; i < ctx.selector().size(); i++) {
c = visitCombinator(ctx.combinator(i - 1));
s = visitSelector(ctx.selector(i));
s.setCombinator(c);
combinedSelector.add(s);
}
// entire ruleset is not valid when selector is not valid
// there is no need to parse selector's when already marked as invalid
if (statement_stack.peek().invalid || combined_selector_stack.peek().invalid) {
combinedSelector = null;
if (log.isDebugEnabled()) {
if (statement_stack.peek().invalid) {
log.debug("Ommiting combined selector, whole statement discarded");
} else {
log.debug("Combined selector is invalid");
}
}
// mark whole ruleset as invalid
statement_stack.peek().invalid = true;
} else {
if (log.isDebugEnabled()) {
log.debug("Returing combined selector: {}.", combinedSelector);
}
}
combined_selector_stack.pop();
logLeave("combined_selector");
return combinedSelector;
}
@Override
/**
combinator
: GREATER S* //child combinator
| PLUS S* //adjacent combinator
| TILDE S* //preceding combinator
| S //descendant combinator
*/
public Selector.Combinator visitCombinator(CSSParser.CombinatorContext ctx) {
logEnter("combinator");
if (ctx.GREATER() != null) {
return Selector.Combinator.CHILD;
} else if (ctx.PLUS() != null) {
return Selector.Combinator.ADJACENT;
} else if (ctx.TILDE() != null) {
return Selector.Combinator.PRECEDING;
} else {
return Selector.Combinator.DESCENDANT;
}
}
protected static class selector_scope {
cz.vutbr.web.css.Selector s;
}
protected Stack selector_stack = new Stack<>();
/**
* selector
* : (IDENT | ASTERISK) selpart* S*
* | selpart+ S*
* ;
*/
public Selector visitSelector(CSSParser.SelectorContext ctx) {
if (ctxHasErrorNode(ctx)) {
statement_stack.peek().invalid = true;
return null;
}
selector_stack.push(new selector_scope());
cz.vutbr.web.css.Selector sel;
logEnter("selector");
selector_stack.peek().s = sel = (cz.vutbr.web.css.Selector) rf.createSelector().unlock();
if (ctx.IDENT() != null || ctx.ASTERISK() != null) {
cz.vutbr.web.css.Selector.ElementName en = rf.createElement(cz.vutbr.web.css.Selector.ElementName.WILDCARD);
if (ctx.IDENT() != null) {
en.setName(extractTextUnescaped(ctx.IDENT().getText()));
}
//log.debug("Adding element name: {}.", en.getName());
selector_stack.peek().s.add(en);
}
for (CSSParser.SelpartContext selpartctx : ctx.selpart()) {
visitSelpart(selpartctx);
}
logLeave("selector");
selector_stack.pop();
return sel;
}
/**
* selpart
* : HASH
* | CLASSKEYWORD
* | LBRACKET S* attribute RBRACKET
* | pseudo
* | INVALID_SELPART // invalid selpart
*/
@Override
public Object visitSelpart(CSSParser.SelpartContext ctx) {
logEnter("selpart");
String ident;
if (ctx.HASH() != null) {
ident = extractIdUnescaped(ctx.HASH().getText());
if (ident != null) {
selector_stack.peek().s.add(rf.createID(ident));
} else {
combined_selector_stack.peek().invalid = true;
}
} else if (ctx.CLASSKEYWORD() != null) {
selector_stack.peek().s.add(rf.createClass(extractTextUnescaped(ctx.CLASSKEYWORD().getText())));
} else if (ctx.attribute() != null) {
Selector.ElementAttribute ea = visitAttribute(ctx.attribute());
selector_stack.peek().s.add(ea);
} else if (ctx.pseudo() != null) {
Selector.SelectorPart p = visitPseudo(ctx.pseudo());
if (p != null) {
if (p instanceof PseudoElement && selector_stack.peek().s.getPseudoElementType() != null) {
log.warn("Invalid selector with multiple pseudo-elements");
combined_selector_stack.peek().invalid = true;
}
else
selector_stack.peek().s.add(p);
} else {
combined_selector_stack.peek().invalid = true;
}
} else {
combined_selector_stack.peek().invalid = true;
}
logLeave("selpart");
//returns null
return null;
}
@Override
/**
* attribute
: IDENT S*
((EQUALS | INCLUDES | DASHMATCH | STARTSWITH | ENDSWITH | CONTAINS) S* (IDENT | string) S*)?
*/
public Selector.ElementAttribute visitAttribute(CSSParser.AttributeContext ctx) {
//attributes can be like [attr] or [attr operator value]
// see http://www.w3.org/TR/CSS2/selector.html#attribute-selectors
logEnter("attribute: ", ctx);
//initialize attribute
String attributeName = extractTextUnescaped(ctx.children.get(0).getText());
String value = null;
boolean isStringValue = false;
Selector.Operator op = Selector.Operator.NO_OPERATOR;
List ctx2 = filterSpaceTokens(ctx.children);
//is attribute like [attr=value]
if (ctx2.size() == 3) {
CommonToken opToken = (CommonToken) ((TerminalNodeImpl) ctx2.get(1)).symbol;
isStringValue = (ctx2.get(2) instanceof CSSParser.StringContext);
if (isStringValue) {
value = ctx2.get(2).getText();
} else {
value = ctx2.get(2).getText();
}
value = extractTextUnescaped(value);
switch (opToken.getType()) {
case CSSParser.EQUALS: {
op = Selector.Operator.EQUALS;
break;
}
case CSSParser.INCLUDES: {
op = Selector.Operator.INCLUDES;
break;
}
case CSSParser.DASHMATCH: {
op = Selector.Operator.DASHMATCH;
break;
}
case CSSParser.CONTAINS: {
op = Selector.Operator.CONTAINS;
break;
}
case CSSParser.STARTSWITH: {
op = Selector.Operator.STARTSWITH;
break;
}
case CSSParser.ENDSWITH: {
op = Selector.Operator.ENDSWITH;
break;
}
default: {
op = Selector.Operator.NO_OPERATOR;
}
}
}
Selector.ElementAttribute elemAttr = null;
if (attributeName != null) {
elemAttr = rf.createAttribute(value, isStringValue, op, attributeName);
} else {
if (log.isDebugEnabled()) {
log.debug("Invalid attribute element in selector");
}
combined_selector_stack.peek().invalid = true;
}
logLeave("attribute");
return elemAttr;
}
@Override
/**
* pseudo
: COLON COLON? (MINUS? IDENT | FUNCTION S* (IDENT | MINUS? NUMBER | MINUS? INDEX) S* RPAREN)
*/
public Selector.SelectorPart visitPseudo(CSSParser.PseudoContext ctx) {
logEnter("pseudo: ", ctx);
boolean isPseudoElem = ctx.COLON().size() > 1;
Selector.SelectorPart pseudo = null;
String name;
if (ctx.FUNCTION() != null) {
// function
name = extractTextUnescaped(ctx.FUNCTION().getText());
if (ctx.selector() != null) {
Selector sel = visitSelector(ctx.selector());
pseudo = (isPseudoElem ? rf.createPseudoElement(name, sel) : rf.createPseudoClass(name, sel));
} else {
String value = (ctx.MINUS() == null ? "" : "-");
if (ctx.IDENT() != null) {
value += ctx.IDENT().getText();
} else if (ctx.NUMBER() != null) {
value += ctx.NUMBER().getText();
} else if (ctx.INDEX() != null) {
value += ctx.INDEX().getText();
} else {
throw new UnsupportedOperationException("unknown state");
}
pseudo = (isPseudoElem ? rf.createPseudoElement(name, value) : rf.createPseudoClass(name, value));
}
} else if (ctx.IDENT() != null) {
// ident
name = extractTextUnescaped(ctx.IDENT().getText());
if (ctx.MINUS() != null) {
name = ctx.MINUS().getText() + name;
}
// Legacy support for :after, :before, :first-line, and :first-letter pseudo-elements
if (!isPseudoElem && ("after".equalsIgnoreCase(name) || "before".equalsIgnoreCase(name) || "first-line".equalsIgnoreCase(name) || "first-letter".equalsIgnoreCase(name))) {
isPseudoElem = true;
}
if (isPseudoElem) {
pseudo = rf.createPseudoElement(name);
} else if (ctx.parent instanceof CSSParser.PageContext) {
pseudo = rf.createPseudoPage(name);
} else {
pseudo = rf.createPseudoClass(name);
}
} else {
// invalid selpart
name = "";
}
if ((pseudo == null) ||
(pseudo instanceof Selector.PseudoPage && ((Selector.PseudoPage) pseudo).getType() == null) ||
(pseudo instanceof Selector.PseudoClass && ((Selector.PseudoClass) pseudo).getType() == null) ||
(pseudo instanceof Selector.PseudoElement && ((Selector.PseudoElement) pseudo).getType() == null)) {
log.error("invalid pseudo declaration: " + name);
pseudo = null; // invalid
}
logLeave("pseudo");
return pseudo;
}
@Override
public String visitString(CSSParser.StringContext ctx) {
if (ctx.INVALID_STRING() != null) {
return null;
}
return extractTextUnescaped(ctx.getText());
}
@Override
public Object visitAny(CSSParser.AnyContext ctx) {
// handled elsewhere
return null;
}
@Override
public Object visitNostatement(CSSParser.NostatementContext ctx) {
// handled elsewhere
return null;
}
@Override
public Object visitNoprop(CSSParser.NopropContext ctx) {
// handled elsewhere
return null;
}
@Override
public Object visitNorule(CSSParser.NoruleContext ctx) {
// handled elsewhere
return null;
}
@Override
public Object visitNomediaquery(CSSParser.NomediaqueryContext ctx) {
// handled elsewhere
return null;
}
@Override
public Object visit(ParseTree parseTree) {
logEnter("visit");
if (log.isDebugEnabled()) {
log.debug(parseTree.getText());
}
// Object o = visitChildren(parseTree.chi);
logLeave("visit");
return null;
}
@Override
public Object visitChildren(RuleNode ruleNode) {
return null;
}
@Override
public Object visitTerminal(TerminalNode terminalNode) {
return null;
}
@Override
public Object visitErrorNode(ErrorNode errorNode) {
log.error("visitErrorNode");
return null;
}
}