EMMA Coverage Report (generated Mon Nov 01 16:48:29 PDT 2010)
[all classes][com.google.caja.parser.css]

COVERAGE SUMMARY FOR SOURCE FILE [CssTree.java]

nameclass, %method, %block, %line, %
CssTree.java98%  (47/48)92%  (217/235)91%  (2293/2518)94%  (557.5/596)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class CssTree100% (1/1)96%  (25/26)92%  (231/252)90%  (53/59)
<static initializer> 100% (1/1)100% (24/24)100% (7/7)
CssTree (FilePosition, Class, List): void 100% (1/1)100% (10/10)100% (3/3)
CssTree (FilePosition, Class, List, CssTree$1): void 100% (1/1)100% (6/6)100% (1/1)
CssTree (FilePosition, List): void 100% (1/1)100% (10/10)100% (3/3)
CssTree (FilePosition, List, CssTree$1): void 100% (1/1)100% (5/5)100% (1/1)
access$1000 (): Pattern 100% (1/1)100% (2/2)100% (1/1)
access$1100 (): Pattern 100% (1/1)100% (2/2)100% (1/1)
access$1200 (): Pattern 100% (1/1)100% (2/2)100% (1/1)
access$1300 (): Pattern 100% (1/1)100% (2/2)100% (1/1)
access$1400 (): Pattern 100% (1/1)100% (2/2)100% (1/1)
access$200 (List, RenderContext): void 100% (1/1)100% (4/4)100% (1/1)
access$300 (String, RenderContext): void 100% (1/1)100% (4/4)100% (1/1)
access$400 (List, FilePosition, RenderContext): void 100% (1/1)100% (5/5)100% (1/1)
access$500 (List, RenderContext): void 100% (1/1)100% (4/4)100% (1/1)
access$700 (): Pattern 100% (1/1)100% (2/2)100% (1/1)
access$800 (): Pattern 100% (1/1)100% (2/2)100% (1/1)
access$900 (String, RenderContext): void 100% (1/1)100% (4/4)100% (1/1)
children (): List 100% (1/1)100% (4/4)100% (1/1)
getValue (): Object 100% (1/1)100% (2/2)100% (1/1)
makeRenderer (Appendable, Callback): TokenConsumer 100% (1/1)100% (9/9)100% (1/1)
renderCommaGroup (List, RenderContext): void 100% (1/1)100% (26/26)100% (7/7)
renderCssIdent (String, RenderContext): void 100% (1/1)100% (13/13)100% (4/4)
renderCssString (String, RenderContext): void 100% (1/1)100% (21/21)100% (6/6)
renderSpaceGroup (List, RenderContext): void 100% (1/1)100% (26/26)100% (7/7)
renderStatements (List, FilePosition, RenderContext): void 100% (1/1)100% (40/40)100% (12/12)
toString (): String 0%   (0/1)0%   (0/21)0%   (0/6)
     
class CssTree$10%   (0/1)100% (0/0)100% (0/0)100% (0/0)
     
class CssTree$Attrib100% (1/1)100% (7/7)98%  (97/99)99%  (20.8/21)
CssTree$Attrib (FilePosition, String, CssTree$AttribOperation, CssTree$CssLit... 100% (1/1)100% (25/25)100% (3/3)
CssTree$Attrib (FilePosition, String, List): void 100% (1/1)100% (9/9)100% (3/3)
getIdent (): String 100% (1/1)100% (3/3)100% (1/1)
getOperation (): CssTree$AttribOperation 100% (1/1)100% (12/12)100% (1/1)
getRhsValue (): CssTree$CssLiteral 100% (1/1)83%  (10/12)83%  (0.8/1)
getValue (): String 100% (1/1)100% (3/3)100% (1/1)
render (RenderContext): void 100% (1/1)100% (35/35)100% (11/11)
     
class CssTree$AttribOperation100% (1/1)100% (4/4)100% (29/29)100% (9/9)
CssTree$AttribOperation (FilePosition, CssTree$AttribOperator): void 100% (1/1)100% (9/9)100% (3/3)
CssTree$AttribOperation (FilePosition, CssTree$AttribOperator, List): void 100% (1/1)100% (5/5)100% (2/2)
getValue (): CssTree$AttribOperator 100% (1/1)100% (3/3)100% (1/1)
render (RenderContext): void 100% (1/1)100% (12/12)100% (3/3)
     
class CssTree$AttribOperator100% (1/1)80%  (4/5)91%  (52/57)98%  (5.9/6)
<static initializer> 100% (1/1)100% (37/37)100% (4/4)
CssTree$AttribOperator (String, int, String): void 100% (1/1)100% (8/8)100% (1/1)
getToken (): String 100% (1/1)100% (3/3)100% (1/1)
valueOf (String): CssTree$AttribOperator 0%   (0/1)0%   (0/5)0%   (0/1)
values (): CssTree$AttribOperator [] 100% (1/1)100% (4/4)100% (1/1)
     
class CssTree$ClassLiteral100% (1/1)100% (4/4)100% (31/31)100% (8/8)
CssTree$ClassLiteral (FilePosition, String): void 100% (1/1)100% (5/5)100% (1/1)
CssTree$ClassLiteral (FilePosition, String, List): void 100% (1/1)100% (5/5)100% (2/2)
checkValue (String): boolean 100% (1/1)100% (5/5)100% (1/1)
render (RenderContext): void 100% (1/1)100% (16/16)100% (4/4)
     
class CssTree$Combination100% (1/1)100% (5/5)100% (37/37)100% (11/11)
CssTree$Combination (FilePosition, CssTree$Combinator): void 100% (1/1)100% (9/9)100% (3/3)
CssTree$Combination (FilePosition, CssTree$Combinator, List): void 100% (1/1)100% (5/5)100% (2/2)
getCombinator (): CssTree$Combinator 100% (1/1)100% (3/3)100% (1/1)
getValue (): CssTree$Combinator 100% (1/1)100% (3/3)100% (1/1)
render (RenderContext): void 100% (1/1)100% (17/17)100% (4/4)
     
class CssTree$Combinator100% (1/1)50%  (3/6)80%  (48/60)80%  (4.8/6)
<static initializer> 100% (1/1)100% (37/37)100% (4/4)
CssTree$Combinator (String, int, String): void 100% (1/1)100% (8/8)100% (1/1)
access$1500 (CssTree$Combinator): String 100% (1/1)100% (3/3)100% (1/1)
getSymbol (): String 0%   (0/1)0%   (0/3)0%   (0/1)
valueOf (String): CssTree$Combinator 0%   (0/1)0%   (0/5)0%   (0/1)
values (): CssTree$Combinator [] 0%   (0/1)0%   (0/4)0%   (0/1)
     
class CssTree$CssExprAtom100% (1/1)100% (2/2)100% (13/13)100% (4/4)
CssTree$CssExprAtom (FilePosition, Class, List): void 100% (1/1)100% (7/7)100% (2/2)
CssTree$CssExprAtom (FilePosition, List): void 100% (1/1)100% (6/6)100% (2/2)
     
class CssTree$CssLiteral100% (1/1)100% (3/3)100% (24/24)100% (8/8)
CssTree$CssLiteral (FilePosition, String): void 100% (1/1)100% (8/8)100% (3/3)
getValue (): String 100% (1/1)100% (3/3)100% (1/1)
setValue (String): void 100% (1/1)100% (13/13)100% (4/4)
     
class CssTree$CssStatement100% (1/1)100% (1/1)100% (6/6)100% (2/2)
CssTree$CssStatement (FilePosition, List): void 100% (1/1)100% (6/6)100% (2/2)
     
class CssTree$Declaration100% (1/1)100% (2/2)100% (11/11)100% (4/4)
CssTree$Declaration (FilePosition, Class, List): void 100% (1/1)100% (6/6)100% (2/2)
CssTree$Declaration (FilePosition, List): void 100% (1/1)100% (5/5)100% (2/2)
     
class CssTree$DeclarationGroup100% (1/1)80%  (4/5)84%  (61/73)84%  (15.1/18)
CssTree$DeclarationGroup (FilePosition, List): void 100% (1/1)100% (7/7)100% (2/2)
CssTree$DeclarationGroup (FilePosition, Void, List): void 0%   (0/1)0%   (0/5)0%   (0/2)
children (): List 100% (1/1)100% (4/4)100% (1/1)
childrenChanged (): void 100% (1/1)72%  (18/25)82%  (4.1/5)
render (RenderContext): void 100% (1/1)100% (32/32)100% (8/8)
     
class CssTree$EmptyDeclaration100% (1/1)100% (4/4)81%  (26/32)93%  (7.4/8)
<static initializer> 100% (1/1)75%  (6/8)75%  (0.8/1)
CssTree$EmptyDeclaration (FilePosition): void 100% (1/1)100% (5/5)100% (2/2)
CssTree$EmptyDeclaration (FilePosition, Void, List): void 100% (1/1)69%  (9/13)89%  (2.7/3)
render (RenderContext): void 100% (1/1)100% (6/6)100% (2/2)
     
class CssTree$Expr100% (1/1)89%  (8/9)78%  (60/77)89%  (12.5/14)
<static initializer> 100% (1/1)75%  (6/8)75%  (0.8/1)
CssTree$Expr (FilePosition, List): void 100% (1/1)100% (6/6)100% (2/2)
CssTree$Expr (FilePosition, Void, List): void 100% (1/1)100% (5/5)100% (2/2)
childrenChanged (): void 100% (1/1)75%  (12/16)91%  (2.7/3)
getNTerms (): int 100% (1/1)100% (8/8)100% (1/1)
getNthOperation (int): CssTree$Operation 100% (1/1)100% (10/10)100% (1/1)
getNthOperator (int): CssTree$Operator 0%   (0/1)0%   (0/11)0%   (0/1)
getNthTerm (int): CssTree$Term 100% (1/1)100% (8/8)100% (1/1)
render (RenderContext): void 100% (1/1)100% (5/5)100% (2/2)
     
class CssTree$FontFace100% (1/1)100% (4/4)88%  (52/59)94%  (14.1/15)
CssTree$FontFace (FilePosition, List): void 100% (1/1)100% (5/5)100% (2/2)
CssTree$FontFace (FilePosition, Void, List): void 100% (1/1)100% (5/5)100% (2/2)
childrenChanged (): void 100% (1/1)72%  (18/25)82%  (4.1/5)
render (RenderContext): void 100% (1/1)100% (24/24)100% (6/6)
     
class CssTree$FunctionCall100% (1/1)100% (8/8)93%  (83/89)98%  (19.5/20)
<static initializer> 100% (1/1)75%  (6/8)75%  (0.8/1)
CssTree$FunctionCall (FilePosition, Name, CssTree$Expr): void 100% (1/1)100% (9/9)100% (3/3)
CssTree$FunctionCall (FilePosition, Name, List): void 100% (1/1)100% (9/9)100% (2/2)
childrenChanged (): void 100% (1/1)80%  (16/20)93%  (2.8/3)
getArguments (): CssTree$Expr 100% (1/1)100% (6/6)100% (1/1)
getName (): Name 100% (1/1)100% (3/3)100% (1/1)
getValue (): Name 100% (1/1)100% (3/3)100% (1/1)
render (RenderContext): void 100% (1/1)100% (31/31)100% (8/8)
     
class CssTree$HashLiteral100% (1/1)100% (5/5)100% (60/60)100% (12/12)
CssTree$HashLiteral (FilePosition, String): void 100% (1/1)100% (5/5)100% (1/1)
CssTree$HashLiteral (FilePosition, String, List): void 100% (1/1)100% (5/5)100% (2/2)
checkValue (String): boolean 100% (1/1)100% (5/5)100% (1/1)
hex (FilePosition, int, int): CssTree$HashLiteral 100% (1/1)100% (34/34)100% (5/5)
render (RenderContext): void 100% (1/1)100% (11/11)100% (3/3)
     
class CssTree$IdLiteral100% (1/1)100% (4/4)100% (31/31)100% (8/8)
CssTree$IdLiteral (FilePosition, String): void 100% (1/1)100% (5/5)100% (1/1)
CssTree$IdLiteral (FilePosition, String, List): void 100% (1/1)100% (5/5)100% (2/2)
checkValue (String): boolean 100% (1/1)100% (5/5)100% (1/1)
render (RenderContext): void 100% (1/1)100% (16/16)100% (4/4)
     
class CssTree$IdentLiteral100% (1/1)100% (4/4)100% (25/25)100% (8/8)
CssTree$IdentLiteral (FilePosition, String): void 100% (1/1)100% (5/5)100% (2/2)
CssTree$IdentLiteral (FilePosition, String, List): void 100% (1/1)100% (5/5)100% (2/2)
checkValue (String): boolean 100% (1/1)100% (5/5)100% (1/1)
render (RenderContext): void 100% (1/1)100% (10/10)100% (3/3)
     
class CssTree$Import100% (1/1)100% (6/6)100% (110/110)100% (26/26)
CssTree$Import (FilePosition, CssTree$UriLiteral, List): void 100% (1/1)100% (8/8)100% (2/2)
CssTree$Import (FilePosition, Void, List): void 100% (1/1)100% (5/5)100% (2/2)
getMedia (): List 100% (1/1)100% (33/33)100% (4/4)
getUri (): CssTree$UriLiteral 100% (1/1)100% (6/6)100% (1/1)
join (List, List): List 100% (1/1)100% (19/19)100% (4/4)
render (RenderContext): void 100% (1/1)100% (39/39)100% (13/13)
     
class CssTree$Media100% (1/1)100% (4/4)100% (105/105)100% (24/24)
CssTree$Media (FilePosition, List): void 100% (1/1)100% (5/5)100% (2/2)
CssTree$Media (FilePosition, Void, List): void 100% (1/1)100% (5/5)100% (2/2)
getMedia (): List 100% (1/1)100% (27/27)100% (5/5)
render (RenderContext): void 100% (1/1)100% (68/68)100% (15/15)
     
class CssTree$Medium100% (1/1)100% (4/4)100% (28/28)100% (9/9)
CssTree$Medium (FilePosition, Name): void 100% (1/1)100% (9/9)100% (3/3)
CssTree$Medium (FilePosition, Name, List): void 100% (1/1)100% (5/5)100% (2/2)
getValue (): Name 100% (1/1)100% (3/3)100% (1/1)
render (RenderContext): void 100% (1/1)100% (11/11)100% (3/3)
     
class CssTree$Operation100% (1/1)100% (5/5)100% (37/37)100% (11/11)
CssTree$Operation (FilePosition, CssTree$Operator): void 100% (1/1)100% (9/9)100% (3/3)
CssTree$Operation (FilePosition, CssTree$Operator, List): void 100% (1/1)100% (5/5)100% (2/2)
getOperator (): CssTree$Operator 100% (1/1)100% (3/3)100% (1/1)
getValue (): CssTree$Operator 100% (1/1)100% (3/3)100% (1/1)
render (RenderContext): void 100% (1/1)100% (17/17)100% (4/4)
     
class CssTree$Operator100% (1/1)67%  (4/6)87%  (62/71)98%  (6.8/7)
<static initializer> 100% (1/1)100% (48/48)100% (5/5)
CssTree$Operator (String, int, String): void 100% (1/1)100% (8/8)100% (1/1)
access$1600 (CssTree$Operator): String 100% (1/1)100% (3/3)100% (1/1)
getSymbol (): String 100% (1/1)100% (3/3)100% (1/1)
valueOf (String): CssTree$Operator 0%   (0/1)0%   (0/5)0%   (0/1)
values (): CssTree$Operator [] 0%   (0/1)0%   (0/4)0%   (0/1)
     
class CssTree$Page100% (1/1)100% (3/3)100% (62/62)100% (17/17)
CssTree$Page (FilePosition, Name, List): void 100% (1/1)100% (8/8)100% (3/3)
getValue (): Name 100% (1/1)100% (3/3)100% (1/1)
render (RenderContext): void 100% (1/1)100% (51/51)100% (13/13)
     
class CssTree$PageElement100% (1/1)100% (2/2)100% (13/13)100% (4/4)
CssTree$PageElement (FilePosition, Class, List): void 100% (1/1)100% (7/7)100% (2/2)
CssTree$PageElement (FilePosition, List): void 100% (1/1)100% (6/6)100% (2/2)
     
class CssTree$Prio100% (1/1)100% (4/4)100% (35/35)100% (10/10)
CssTree$Prio (FilePosition, String): void 100% (1/1)100% (9/9)100% (3/3)
CssTree$Prio (FilePosition, String, List): void 100% (1/1)100% (5/5)100% (2/2)
getValue (): String 100% (1/1)100% (3/3)100% (1/1)
render (RenderContext): void 100% (1/1)100% (18/18)100% (4/4)
     
class CssTree$ProgId100% (1/1)100% (5/5)100% (75/75)100% (19/19)
CssTree$ProgId (FilePosition, Name, List): void 100% (1/1)100% (9/9)100% (3/3)
children (): List 100% (1/1)100% (4/4)100% (1/1)
getName (): Name 100% (1/1)100% (3/3)100% (1/1)
getValue (): Name 100% (1/1)100% (3/3)100% (1/1)
render (RenderContext): void 100% (1/1)100% (56/56)100% (13/13)
     
class CssTree$ProgIdAttribute100% (1/1)100% (7/7)86%  (65/76)93%  (18.7/20)
CssTree$ProgIdAttribute (FilePosition, Name, List): void 100% (1/1)100% (10/10)100% (3/3)
children (): List 100% (1/1)100% (4/4)100% (1/1)
childrenChanged (): void 100% (1/1)63%  (19/30)81%  (5.7/7)
getName (): Name 100% (1/1)100% (3/3)100% (1/1)
getPropertyValue (): CssTree$Term 100% (1/1)100% (6/6)100% (1/1)
getValue (): Name 100% (1/1)100% (3/3)100% (1/1)
render (RenderContext): void 100% (1/1)100% (20/20)100% (6/6)
     
class CssTree$Property100% (1/1)100% (5/5)100% (31/31)100% (10/10)
CssTree$Property (FilePosition, Name): void 100% (1/1)100% (9/9)100% (3/3)
CssTree$Property (FilePosition, Name, List): void 100% (1/1)100% (5/5)100% (2/2)
getPropertyName (): Name 100% (1/1)100% (3/3)100% (1/1)
getValue (): Name 100% (1/1)100% (3/3)100% (1/1)
render (RenderContext): void 100% (1/1)100% (11/11)100% (3/3)
     
class CssTree$PropertyDeclaration100% (1/1)88%  (7/8)92%  (100/109)94%  (22.5/24)
<static initializer> 100% (1/1)75%  (6/8)75%  (0.8/1)
CssTree$PropertyDeclaration (FilePosition, List): void 100% (1/1)100% (5/5)100% (2/2)
CssTree$PropertyDeclaration (FilePosition, Void, List): void 100% (1/1)100% (5/5)100% (2/2)
childrenChanged (): void 100% (1/1)92%  (44/48)97%  (6.8/7)
getExpr (): CssTree$Expr 100% (1/1)100% (3/3)100% (1/1)
getPrio (): CssTree$Prio 0%   (0/1)0%   (0/3)0%   (0/1)
getProperty (): CssTree$Property 100% (1/1)100% (3/3)100% (1/1)
render (RenderContext): void 100% (1/1)100% (34/34)100% (9/9)
     
class CssTree$Pseudo100% (1/1)100% (3/3)100% (32/32)100% (8/8)
CssTree$Pseudo (FilePosition, CssTree$CssExprAtom): void 100% (1/1)100% (7/7)100% (2/2)
CssTree$Pseudo (FilePosition, Void, List): void 100% (1/1)100% (8/8)100% (2/2)
render (RenderContext): void 100% (1/1)100% (17/17)100% (4/4)
     
class CssTree$PseudoPage100% (1/1)100% (4/4)100% (31/31)100% (10/10)
CssTree$PseudoPage (FilePosition, Name): void 100% (1/1)100% (8/8)100% (3/3)
CssTree$PseudoPage (FilePosition, Name, List): void 100% (1/1)100% (5/5)100% (2/2)
getValue (): Name 100% (1/1)100% (3/3)100% (1/1)
render (RenderContext): void 100% (1/1)100% (15/15)100% (4/4)
     
class CssTree$QuantityLiteral100% (1/1)100% (4/4)100% (26/26)100% (8/8)
CssTree$QuantityLiteral (FilePosition, String): void 100% (1/1)100% (5/5)100% (2/2)
CssTree$QuantityLiteral (FilePosition, String, List): void 100% (1/1)100% (5/5)100% (2/2)
checkValue (String): boolean 100% (1/1)100% (5/5)100% (1/1)
render (RenderContext): void 100% (1/1)100% (11/11)100% (3/3)
     
class CssTree$RuleSet100% (1/1)100% (3/3)98%  (62/63)100% (13/13)
CssTree$RuleSet (FilePosition, List): void 100% (1/1)100% (5/5)100% (2/2)
CssTree$RuleSet (FilePosition, Void, List): void 100% (1/1)100% (5/5)100% (2/2)
render (RenderContext): void 100% (1/1)98%  (52/53)100% (9/9)
     
class CssTree$Selector100% (1/1)100% (4/4)84%  (56/67)90%  (17.1/19)
CssTree$Selector (FilePosition, List): void 100% (1/1)100% (6/6)100% (2/2)
CssTree$Selector (FilePosition, Void, List): void 100% (1/1)100% (5/5)100% (2/2)
childrenChanged (): void 100% (1/1)78%  (40/51)86%  (11.1/13)
render (RenderContext): void 100% (1/1)100% (5/5)100% (2/2)
     
class CssTree$SimpleSelector100% (1/1)100% (4/4)100% (42/42)100% (10/10)
CssTree$SimpleSelector (FilePosition, List): void 100% (1/1)100% (6/6)100% (2/2)
CssTree$SimpleSelector (FilePosition, Void, List): void 100% (1/1)100% (5/5)100% (2/2)
getElementName (): String 100% (1/1)100% (15/15)100% (4/4)
render (RenderContext): void 100% (1/1)100% (16/16)100% (2/2)
     
class CssTree$StringLiteral100% (1/1)100% (4/4)96%  (25/26)98%  (6.8/7)
CssTree$StringLiteral (FilePosition, String): void 100% (1/1)100% (5/5)100% (1/1)
CssTree$StringLiteral (FilePosition, String, List): void 100% (1/1)100% (5/5)100% (2/2)
checkValue (String): boolean 100% (1/1)83%  (5/6)83%  (0.8/1)
render (RenderContext): void 100% (1/1)100% (10/10)100% (3/3)
     
class CssTree$StyleSheet100% (1/1)100% (3/3)100% (27/27)100% (7/7)
CssTree$StyleSheet (FilePosition, List): void 100% (1/1)100% (6/6)100% (2/2)
CssTree$StyleSheet (FilePosition, Void, List): void 100% (1/1)100% (5/5)100% (2/2)
render (RenderContext): void 100% (1/1)100% (16/16)100% (3/3)
     
class CssTree$Substitution100% (1/1)50%  (3/6)31%  (21/68)38%  (6/16)
CssTree$Substitution (FilePosition, String): void 100% (1/1)100% (5/5)100% (2/2)
CssTree$Substitution (FilePosition, String, List): void 0%   (0/1)0%   (0/5)0%   (0/2)
checkValue (String): boolean 100% (1/1)100% (5/5)100% (1/1)
getBody (): String 0%   (0/1)0%   (0/31)0%   (0/6)
getSuffix (): String 0%   (0/1)0%   (0/11)0%   (0/2)
render (RenderContext): void 100% (1/1)100% (11/11)100% (3/3)
     
class CssTree$Term100% (1/1)100% (6/6)100% (51/51)100% (13/13)
CssTree$Term (FilePosition, CssTree$UnaryOperator, CssTree$CssExprAtom): void 100% (1/1)100% (10/10)100% (3/3)
CssTree$Term (FilePosition, CssTree$UnaryOperator, List): void 100% (1/1)100% (9/9)100% (2/2)
getExprAtom (): CssTree$CssExprAtom 100% (1/1)100% (6/6)100% (1/1)
getOperator (): CssTree$UnaryOperator 100% (1/1)100% (3/3)100% (1/1)
getValue (): CssTree$UnaryOperator 100% (1/1)100% (3/3)100% (1/1)
render (RenderContext): void 100% (1/1)100% (20/20)100% (5/5)
     
class CssTree$UnaryOperator100% (1/1)50%  (3/6)76%  (37/49)75%  (3.8/5)
<static initializer> 100% (1/1)100% (26/26)100% (3/3)
CssTree$UnaryOperator (String, int, String): void 100% (1/1)100% (8/8)100% (1/1)
access$600 (CssTree$UnaryOperator): String 100% (1/1)100% (3/3)100% (1/1)
getSymbol (): String 0%   (0/1)0%   (0/3)0%   (0/1)
valueOf (String): CssTree$UnaryOperator 0%   (0/1)0%   (0/5)0%   (0/1)
values (): CssTree$UnaryOperator [] 0%   (0/1)0%   (0/4)0%   (0/1)
     
class CssTree$UnicodeRangeLiteral100% (1/1)75%  (3/4)58%  (15/26)62%  (5/8)
CssTree$UnicodeRangeLiteral (FilePosition, String): void 100% (1/1)100% (5/5)100% (2/2)
CssTree$UnicodeRangeLiteral (FilePosition, String, List): void 100% (1/1)100% (5/5)100% (2/2)
checkValue (String): boolean 100% (1/1)100% (5/5)100% (1/1)
render (RenderContext): void 0%   (0/1)0%   (0/11)0%   (0/3)
     
class CssTree$UriLiteral100% (1/1)100% (4/4)89%  (47/53)80%  (16/20)
CssTree$UriLiteral (FilePosition, String, List): void 100% (1/1)100% (6/6)100% (2/2)
CssTree$UriLiteral (FilePosition, URI): void 100% (1/1)100% (6/6)100% (2/2)
checkValue (String): boolean 100% (1/1)62%  (5/8)50%  (2/4)
render (RenderContext): void 100% (1/1)91%  (30/33)83%  (10/12)
     
class CssTree$UserAgent100% (1/1)80%  (4/5)90%  (47/52)98%  (4.9/5)
<static initializer> 100% (1/1)100% (34/34)100% (4/4)
CssTree$UserAgent (String, int): void 100% (1/1)100% (5/5)100% (1/1)
ie7OrOlder (): EnumSet 100% (1/1)100% (4/4)100% (1/1)
valueOf (String): CssTree$UserAgent 0%   (0/1)0%   (0/5)0%   (0/1)
values (): CssTree$UserAgent [] 100% (1/1)100% (4/4)100% (1/1)
     
class CssTree$UserAgentHack100% (1/1)100% (5/5)78%  (50/64)92%  (14.7/16)
CssTree$UserAgentHack (FilePosition, Set, List): void 100% (1/1)100% (10/10)100% (3/3)
childrenChanged (): void 100% (1/1)52%  (15/29)78%  (4.7/6)
getDeclaration (): CssTree$PropertyDeclaration 100% (1/1)100% (6/6)100% (1/1)
getValue (): EnumSet 100% (1/1)100% (4/4)100% (1/1)
render (RenderContext): void 100% (1/1)100% (15/15)100% (5/5)
     
class CssTree$WildcardElement100% (1/1)100% (3/3)100% (24/24)100% (9/9)
CssTree$WildcardElement (FilePosition): void 100% (1/1)100% (6/6)100% (2/2)
CssTree$WildcardElement (FilePosition, Void, List): void 100% (1/1)100% (4/4)100% (2/2)
render (RenderContext): void 100% (1/1)100% (14/14)100% (5/5)

1// Copyright (C) 2006 Google Inc.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//      http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14 
15package com.google.caja.parser.css;
16 
17import com.google.caja.SomethingWidgyHappenedError;
18import com.google.caja.lexer.FilePosition;
19import com.google.caja.lexer.TokenConsumer;
20import com.google.caja.lexer.escaping.Escaping;
21import com.google.caja.lexer.escaping.UriUtil;
22import com.google.caja.parser.AbstractParseTreeNode;
23import com.google.caja.render.Concatenator;
24import com.google.caja.render.CssPrettyPrinter;
25import com.google.caja.reporting.MessageContext;
26import com.google.caja.reporting.RenderContext;
27import com.google.caja.util.Callback;
28import com.google.caja.util.Name;
29 
30import java.io.IOException;
31import java.net.URI;
32import java.net.URISyntaxException;
33import java.util.ArrayList;
34import java.util.Arrays;
35import java.util.Collections;
36import java.util.EnumSet;
37import java.util.List;
38import java.util.Locale;
39import java.util.Set;
40import java.util.regex.Pattern;
41 
42/**
43 * A node in a CSS parse tree.
44 *
45 * @author mikesamuel@gmail.com
46 */
47public abstract class CssTree extends AbstractParseTreeNode {
48  private CssTree(FilePosition pos, List<? extends CssTree> children) {
49    super(pos, CssTree.class);
50    createMutation().appendChildren(children).execute();
51  }
52  private <T extends CssTree> CssTree(
53      FilePosition pos, Class<T> subType, List<? extends T> children) {
54    super(pos, subType);
55    createMutation().appendChildren(children).execute();
56  }
57 
58  @Override
59  public Object getValue() {
60    return null;
61  }
62 
63  @Override
64  public List<? extends CssTree> children() {
65    return childrenAs(CssTree.class);
66  }
67 
68  @Override
69  public String toString() {
70    StringBuilder sb = new StringBuilder();
71    try {
72      formatSelf(new MessageContext(), 0, sb);
73    } catch (IOException ex) {
74      throw new SomethingWidgyHappenedError(
75          "StringBuilders shouldn't throw IOExceptions");
76    }
77    return sb.toString();
78  }
79 
80  public final TokenConsumer makeRenderer(
81      Appendable out, Callback<IOException> exHandler) {
82    return new CssPrettyPrinter(new Concatenator(out, exHandler));
83  }
84 
85 
86  /**
87   * The top level parsetree node.
88   * <pre>
89   * stylesheet
90   *   : [ CHARSET_SYM S* STRING S* ';' ]?
91   *     [S|CDO|CDC]* [ import [S|CDO|CDC]* ]*
92   *     [ [ ruleset | media | page | font_face ] [S|CDO|CDC]* ]*
93   * </pre>
94   */
95  public static final class StyleSheet extends CssTree {
96    /** @param novalue ignored but required for reflection. */
97    @ReflectiveCtor
98    public StyleSheet(
99        FilePosition pos, Void novalue, List<? extends CssStatement> rulesets) {
100      this(pos, rulesets);
101    }
102 
103    public StyleSheet(FilePosition pos, List<? extends CssStatement> rulesets) {
104      super(pos, rulesets);
105    }
106 
107    public void render(RenderContext r) {
108      for (CssTree child : children()) {
109        child.render(r);
110      }
111    }
112  }
113 
114  /**
115   * A root node with no equivalent in the grammar.
116   * This node is like a ruleset, but without the selector, so it works as
117   * the root node of CSS parsed from an XHTML <code>style</code> attribute.
118   */
119  public static final class DeclarationGroup extends CssTree {
120    /** @param novalue ignored but required for reflection. */
121    @ReflectiveCtor
122    public DeclarationGroup(
123        FilePosition pos, Void novalue, List<? extends Declaration> decls) {
124      this(pos, decls);
125    }
126 
127    public DeclarationGroup(
128        FilePosition pos, List<? extends Declaration> decls) {
129      super(pos, Declaration.class, decls);
130    }
131 
132    @Override
133    public List<? extends Declaration> children() {
134      return childrenAs(Declaration.class);
135    }
136 
137    @Override
138    protected void childrenChanged() {
139      super.childrenChanged();
140      for (CssTree child : children()) {
141        if (!(child instanceof Declaration)) {
142          throw new ClassCastException(child.getClass().getName());
143        }
144      }
145    }
146 
147    public void render(RenderContext r) {
148      r.getOut().mark(getFilePosition());
149      boolean first = true;
150      for (Declaration d : children()) {
151        if (!first) {
152          r.getOut().consume(";");
153        } else {
154          first = false;
155        }
156        d.render(r);
157      }
158    }
159  }
160 
161  /** Part of a stylesheet. */
162  public abstract static class CssStatement extends CssTree {
163    CssStatement(FilePosition pos, List<? extends CssTree> children) {
164      super(pos, children);
165    }
166  }
167  /**
168   * <pre>
169   * import
170   *   : IMPORT_SYM S*
171   *     [STRING|URI] S* [ medium [ ',' S* medium]* ]? ';' S*
172   * </pre>
173   */
174  public static final class Import extends CssStatement {
175    private static <T> List<T> join(List<? extends T> a, List<? extends T> b) {
176      List<T> l = new ArrayList<T>(a.size() + b.size());
177      l.addAll(a);
178      l.addAll(b);
179      return l;
180    }
181 
182    /** @param novalue ignored but required for reflection. */
183    @ReflectiveCtor
184    public Import(
185        FilePosition pos, Void novalue, List<? extends Medium> media) {
186      super(pos, media);
187    }
188 
189    public Import(
190        FilePosition pos, UriLiteral uri, List<? extends Medium> media) {
191      super(pos, join(Collections.singletonList(uri), media));
192    }
193 
194    public UriLiteral getUri() { return (UriLiteral) children().get(0); }
195    public List<Medium> getMedia() {
196      List<Medium> media = new ArrayList<Medium>(children().size() - 1);
197      for (CssTree t : children().subList(1, children().size())) {
198        media.add((Medium) t);
199      }
200      return media;
201    }
202 
203    public void render(RenderContext r) {
204      TokenConsumer out = r.getOut();
205      out.mark(getFilePosition());
206      out.consume("@");
207      out.consume("import");
208      out.consume(" ");
209      getUri().render(r);
210 
211      List<? extends CssTree> media = getMedia();
212      if (!media.isEmpty()) {
213        out.consume(" ");
214        renderCommaGroup(media, r);
215      }
216      out.consume(";");
217      out.consume("\n");
218    }
219  }
220 
221  /**
222   * <pre>
223   * media
224   *   : MEDIA_SYM S* medium [ ',' S* medium ]* '{' S* ruleset* '}' S*
225   * </pre>
226   */
227  public static final class Media extends CssStatement {
228    /** @param novalue ignored but required for reflection. */
229    @ReflectiveCtor
230    public Media(
231        FilePosition pos, Void novalue,
232        List<? extends CssTree> mediaAndRuleset) {
233      this(pos, mediaAndRuleset);
234    }
235    public Media(FilePosition pos, List<? extends CssTree> mediaAndRuleset) {
236      super(pos, mediaAndRuleset);
237    }
238 
239    public List<Medium> getMedia() {
240      List<Medium> media = new ArrayList<Medium>();
241      for (CssTree t : children()) {
242        if (!(t instanceof Medium)) { break; }
243        media.add((Medium) t);
244      }
245      return media;
246    }
247 
248    public void render(RenderContext r) {
249      TokenConsumer out = r.getOut();
250      out.mark(getFilePosition());
251      out.consume("@");
252      out.consume("media");
253      out.consume(" ");
254      List<? extends CssTree> children = children();
255      int i = 0;
256      while (i < children.size() && children.get(i) instanceof Medium) { ++i; }
257      renderCommaGroup(children.subList(0, i), r);
258      out.consume("{");
259      for (CssTree ruleset : children.subList(i, children.size())) {
260        ruleset.render(r);
261      }
262      out.mark(FilePosition.endOfOrNull(getFilePosition()));
263      out.consume("}");
264    }
265  }
266 
267  /**
268   * <pre>
269   * medium
270   *   : IDENT S*
271   * </pre>
272   */
273  public static final class Medium extends CssTree {
274    final Name ident;
275 
276    /** @param none ignored but required for reflection. */
277    @ReflectiveCtor
278    public Medium(FilePosition pos, Name ident, List<? extends CssTree> none) {
279      this(pos, ident);
280    }
281    public Medium(FilePosition pos, Name ident) {
282      super(pos, Collections.<CssTree>emptyList());
283      this.ident = ident;
284    }
285 
286    @Override
287    public Name getValue() { return ident; }
288 
289    public void render(RenderContext r) {
290      r.getOut().mark(getFilePosition());
291      renderCssIdent(ident.getCanonicalForm(), r);
292    }
293  }
294 
295  /**
296   * <pre>
297   * page
298   *   : PAGE_SYM S* IDENT? pseudo_page? S*
299   *     '{' S* declaration [ ';' S* declaration ]* '}' S*
300   * </pre>
301   */
302  public static final class Page extends CssStatement {
303    final Name ident;
304 
305    @ReflectiveCtor
306    public Page(
307        FilePosition pos, Name ident, List<? extends PageElement> decls) {
308      super(pos, decls);
309      this.ident = ident;
310    }
311 
312    @Override
313    public Name getValue() { return ident; }
314 
315    public void render(RenderContext r) {
316      TokenConsumer out = r.getOut();
317      out.mark(getFilePosition());
318      out.consume("@");
319      out.consume("page");
320      if (null != ident) {
321        out.consume(" ");
322        renderCssIdent(ident.getCanonicalForm(), r);
323      }
324      List<? extends CssTree> children = children();
325      if (children.get(0) instanceof PseudoPage) {
326        children.get(0).render(r);
327        children = children.subList(1, children.size());
328      }
329      renderStatements(children, getFilePosition(), r);
330    }
331  }
332 
333  /**
334   * A part of a CSS statement.
335   */
336  public abstract static class PageElement extends CssTree {
337    PageElement(FilePosition pos, List<? extends CssTree> children) {
338      super(pos, children);
339    }
340    <T extends CssTree> PageElement(
341        FilePosition pos, Class<T> childType, List<? extends T> children) {
342      super(pos, childType, children);
343    }
344  }
345 
346  /**
347   * <pre>
348   * pseudo_page
349   *   : ':' IDENT
350   * </pre>
351   */
352  public static final class PseudoPage extends PageElement {
353    final Name ident;
354 
355    /** @param none ignored but required for reflection. */
356    @ReflectiveCtor
357    public PseudoPage(
358        FilePosition pos, Name ident, List<? extends CssTree> none) {
359      this(pos, ident);
360    }
361    public PseudoPage(FilePosition pos, Name ident) {
362      super(pos, Collections.<CssTree>emptyList());
363      this.ident = ident;
364    }
365 
366    @Override
367    public Name getValue() { return ident; }
368 
369    public void render(RenderContext r) {
370      r.getOut().mark(getFilePosition());
371      r.getOut().consume(":");
372      renderCssIdent(ident.getCanonicalForm(), r);
373    }
374  }
375 
376  /**
377   * <pre>
378   * font_face
379   *   : FONT_FACE_SYM S*
380   *     '{' S* declaration [ ';' S* declaration ]* '}' S*
381   * </pre>
382   */
383  public static final class FontFace extends CssStatement {
384    /** @param novalue ignored but required for reflection. */
385    @ReflectiveCtor
386    public FontFace(
387        FilePosition pos, Void novalue, List<? extends Declaration> decls) {
388      this(pos, decls);
389    }
390 
391    public FontFace(FilePosition pos, List<? extends Declaration> decls) {
392      super(pos, decls);
393    }
394 
395    @Override
396    protected void childrenChanged() {
397      super.childrenChanged();
398      for (CssTree child : children()) {
399        if (!(child instanceof Declaration)) {
400          throw new ClassCastException(child.getClass().getName());
401        }
402      }
403    }
404 
405    public void render(RenderContext r) {
406      r.getOut().mark(getFilePosition());
407      r.getOut().consume("@");
408      r.getOut().consume("font-face");
409      r.getOut().consume(" ");
410      renderStatements(children(), getFilePosition(), r);
411    }
412  }
413 
414  /**
415   * <pre>
416   * property
417   *   : IDENT S*
418   * </pre>
419   */
420  public static final class Property extends CssTree {
421    private final Name ident;
422    /** @param none ignored but required for reflection. */
423    @ReflectiveCtor
424    public Property(
425        FilePosition pos, Name ident, List<? extends CssTree> none) {
426      this(pos, ident);
427    }
428 
429    public Property(FilePosition pos, Name ident) {
430      super(pos, Collections.<CssTree>emptyList());
431      this.ident = ident;
432    }
433 
434    @Override
435    public Name getValue() { return ident; }
436 
437    public Name getPropertyName() {
438      return ident;
439    }
440 
441    public void render(RenderContext r) {
442      r.getOut().mark(getFilePosition());
443      renderCssIdent(ident.getCanonicalForm(), r);
444    }
445  }
446 
447  /**
448   * <pre>
449   * ruleset
450   *   : selector [ ',' S* selector ]*
451   *     '{' S* declaration [ ';' S* declaration ]* '}' S*
452   * </pre>
453   */
454  public static final class RuleSet extends CssStatement {
455    /** @param novalue ignored but required for reflection. */
456    @ReflectiveCtor
457    public RuleSet(
458        FilePosition pos, Void novalue,
459        List<? extends CssTree> selectorsAndDecls) {
460      this(pos, selectorsAndDecls);
461    }
462    public RuleSet(
463        FilePosition pos, List<? extends CssTree> selectorsAndDecls) {
464      super(pos, selectorsAndDecls);
465    }
466 
467    public void render(RenderContext r) {
468      List<? extends CssTree> children = children();
469      int i = 0;
470      while (i < children.size() && !(children.get(i) instanceof Declaration)) {
471        ++i;
472      }
473      renderCommaGroup(children.subList(0, i), r);
474      FilePosition selectorEnd = children.get(i - 1).getFilePosition();
475      FilePosition pos = selectorEnd != null && getFilePosition() != null
476          ? FilePosition.span(FilePosition.endOf(selectorEnd),
477                              FilePosition.endOf(getFilePosition()))
478          : null;
479      renderStatements(children.subList(i, children.size()), pos, r);
480    }
481  }
482 
483  /**
484   * <pre>
485   * selector
486   *   : simple_selector [ combinator simple_selector ]*
487   * </pre>
488   */
489  public static final class Selector extends CssTree {
490    /** @param novalue ignored but required for reflection. */
491    @ReflectiveCtor
492    public Selector(
493        FilePosition pos, Void novalue, List<? extends CssTree> children) {
494      this(pos, children);
495    }
496    public Selector(FilePosition pos, List<? extends CssTree> children) {
497      super(pos, children);
498    }
499    public void render(RenderContext r) {
500      renderSpaceGroup(children(), r);
501    }
502    @Override
503    protected void childrenChanged() {
504      super.childrenChanged();
505      List<? extends CssTree> children = children();
506      int n = children.size();
507      boolean needSelector = true;
508      for (CssTree child : children) {
509        if (child instanceof SimpleSelector) {
510          needSelector = false;
511        } else if (!needSelector && child instanceof Combination) {
512          needSelector = true;
513        } else {
514          throw new ClassCastException(child.getClass().getName());
515        }
516      }
517      if (needSelector && n != 0) {
518        throw new IllegalArgumentException();
519      }
520    }
521  }
522 
523  /**
524   * <pre>
525   * simple_selector
526   *   : element_name? [ HASH | class | attrib | pseudo ]* S*
527   * </pre>
528   */
529  public static final class SimpleSelector extends CssTree {
530    /** @param novalue ignored but required for reflection. */
531    @ReflectiveCtor
532    public SimpleSelector(
533        FilePosition pos, Void novalue, List<? extends CssTree> children) {
534      this(pos, children);
535    }
536 
537    public SimpleSelector(FilePosition pos, List<? extends CssTree> children) {
538      super(pos, children);
539    }
540 
541    public String getElementName() {
542      CssTree first = children().get(0);
543      if (first instanceof IdentLiteral) {
544        return ((IdentLiteral) first).getValue();
545      }
546      return null;
547    }
548 
549    public void render(RenderContext r) {
550      // no spaces between because space is the DESCENDANT operator in Selector
551      for (CssTree child : children()) { child.render(r); }
552    }
553  }
554 
555  /**
556   * <pre>
557   * element_name
558   *   : IDENT | '*'
559   * </pre>
560   */
561  public static final class WildcardElement extends CssTree {
562    /**
563     * @param novalue ignored but required for reflection.
564     * @param none ignored but required for reflection.
565     */
566    @ReflectiveCtor
567    public WildcardElement(
568        FilePosition pos, Void novalue, List<? extends CssTree> none) {
569      this(pos);
570    }
571 
572    public WildcardElement(FilePosition pos) {
573      super(pos, Collections.<CssTree>emptyList());
574    }
575 
576    public void render(RenderContext r) {
577      // Start with a space to make sure that rendering couldn't introduce a
578      // comment.  I know of no parse tree that would otherwise do this, but
579      // comments could be used to introduce security holes.
580      TokenConsumer out = r.getOut();
581      out.mark(getFilePosition());
582      out.consume(" ");
583      out.consume("*");
584    }
585  }
586 
587  /**
588   * <pre>
589   * attrib
590   *   : '[' S* IDENT S* [ [ '=' | INCLUDES | DASHMATCH ] S*
591   *     [ IDENT | STRING ] S* ]? ']'
592   * </pre>
593   */
594  public static final class Attrib extends CssTree {
595    final String ident;
596 
597    @ReflectiveCtor
598    public Attrib(
599        FilePosition pos, String ident,
600        List<? extends CssTree> operatorAndValue) {
601      super(pos, operatorAndValue);
602      this.ident = ident;
603    }
604 
605    public Attrib(FilePosition pos, String ident,
606                  AttribOperation operator, CssLiteral value) {
607      super(pos, null == operator
608            ? Collections.<CssTree>emptyList()
609            : Collections.unmodifiableList(Arrays.asList(operator, value)));
610      this.ident = ident;
611    }
612 
613    @Override
614    public String getValue() { return ident; }
615    public String getIdent() { return ident; }
616 
617    public AttribOperation getOperation() {
618      return children().isEmpty() ? null : (AttribOperation) children().get(0);
619    }
620 
621    public CssLiteral getRhsValue() {
622      return children().isEmpty() ? null : (CssLiteral) children().get(1);
623    }
624 
625    public void render(RenderContext r) {
626      TokenConsumer out = r.getOut();
627      out.mark(getFilePosition());
628      out.consume("[");
629      renderCssIdent(ident, r);
630      List<? extends CssTree> children = children();
631      if (!children.isEmpty()) {
632        out.consume(" ");
633        renderSpaceGroup(children, r);
634      }
635      out.mark(FilePosition.endOfOrNull(getFilePosition()));
636      out.consume("]");
637    }
638  }
639 
640  /** Operator used in {@link Attrib} */
641  public static enum AttribOperator {
642    EQUAL("="),
643    INCLUDES("~="),
644    DASHMATCH("|="),
645    ;
646    private final String op;
647    AttribOperator(String op) { this.op = op; }
648 
649    public String getToken() { return op; }
650  }
651 
652  /** <pre>[ '=' | INCLUDES | DASHMATCH ]</pre> */
653  public static final class AttribOperation extends CssTree {
654    final AttribOperator op;
655    /** @param none ignored but required for reflection. */
656    @ReflectiveCtor
657    public AttribOperation(
658        FilePosition pos, AttribOperator op, List<? extends CssTree> none) {
659      this(pos, op);
660    }
661 
662    public AttribOperation(FilePosition pos, AttribOperator op) {
663      super(pos, Collections.<CssTree>emptyList());
664      this.op = op;
665    }
666 
667    @Override
668    public AttribOperator getValue() { return op; }
669 
670    public void render(RenderContext r) {
671      r.getOut().mark(getFilePosition());
672      r.getOut().consume(op.getToken());
673    }
674  }
675 
676  /**
677   * <pre>
678   * pseudo
679   *   : ':' [ IDENT | FUNCTION S* IDENT S* ')' ]
680   * </pre>
681   */
682  public static final class Pseudo extends CssTree {
683    /** @param novalue ignored but required for reflection. */
684    @ReflectiveCtor
685    public Pseudo(
686        FilePosition pos, Void novalue, List<? extends CssExprAtom> oneAtom) {
687      this(pos, oneAtom.get(0));
688    }
689 
690    public Pseudo(FilePosition pos, CssExprAtom child) {
691      super(pos, Collections.singletonList(child));
692    }
693 
694    public void render(RenderContext r) {
695      r.getOut().mark(getFilePosition());
696      r.getOut().consume(":");
697      children().get(0).render(r);
698    }
699  }
700 
701  /**
702   * A CSS property name, style value pair.
703   * <pre>
704   * declaration
705   *   : property-declaration
706   *   | empty-declaration
707   *   | user-agent-hack
708   * </pre>
709   * The term "declaration" is used in the CSS2 spec to describe both
710   * {@link PropertyDeclaration} and {@link EmptyDeclaration}.  Neither
711   * of those terms appear in the spec, and the <code>user-agent-hack</code>
712   * has no analog in the spec since it models a browser hack.
713   */
714  public static abstract class Declaration extends PageElement {
715    Declaration(FilePosition pos, List<? extends CssTree> children) {
716      super(pos, children);
717    }
718 
719    <T extends CssTree> Declaration(
720        FilePosition pos, Class<T> childType, List<? extends T> children) {
721      super(pos, childType, children);
722    }
723  }
724 
725  /**
726   * <pre>
727   * empty-declaration
728   *   : <i>empty</i>
729   * </pre>
730   */
731  public static final class EmptyDeclaration extends Declaration {
732    /**
733     * @param novalue ignored but required for reflection.
734     * @param none ignored but required for reflection.
735     */
736    @ReflectiveCtor
737    public EmptyDeclaration(
738        FilePosition pos, Void novalue, List<? extends CssTree> none) {
739      this(pos);
740      assert none.isEmpty();
741    }
742 
743    public EmptyDeclaration(FilePosition pos) {
744      super(pos, Collections.<CssTree>emptyList());
745    }
746 
747    public void render(RenderContext r) {
748      r.getOut().mark(getFilePosition());
749    }
750  }
751 
752  /**
753   * <pre>
754   * property-declaration
755   *   : property ':' S* expr prio?
756   * </pre>
757   */
758  public static final class PropertyDeclaration extends Declaration {
759    private Property prop;
760    private Expr expr;
761    private Prio prio;
762 
763    /** @param novalue ignored but required for reflection. */
764    @ReflectiveCtor
765    public PropertyDeclaration(
766        FilePosition pos, Void novalue, List<? extends CssTree> children) {
767      this(pos, children);
768    }
769 
770    public PropertyDeclaration(
771        FilePosition pos, List<? extends CssTree> children) {
772      super(pos, children);
773    }
774 
775    @Override
776    protected void childrenChanged() {
777      super.childrenChanged();
778      List<? extends CssTree> children = children();
779      prop = (Property) children.get(0);
780      expr = (Expr) children.get(1);
781      prio = children.size() > 2 ? (Prio) children.get(2) : null;
782      assert children.size() <= 3 && null != prop && null != expr;
783    }
784 
785    public Property getProperty() { return prop; }
786    public Expr getExpr() { return expr; }
787    public Prio getPrio() { return prio; }
788 
789    public void render(RenderContext r) {
790      r.getOut().mark(getFilePosition());
791      prop.render(r);
792      r.getOut().consume(":");
793      r.getOut().consume(" ");
794      expr.render(r);
795      if (null != prio) {
796        r.getOut().consume(" ");
797        prio.render(r);
798      }
799    }
800  }
801 
802  /**
803   * <pre>
804   * prio
805   *   : IMPORTANT_SYM S*
806   * </pre>
807   */
808  public static final class Prio extends CssTree {
809    final String value;
810 
811    /** @param none ignored but required for reflection. */
812    @ReflectiveCtor
813    public Prio(FilePosition pos, String value, List<? extends CssTree> none) {
814      this(pos, value);
815    }
816 
817    public Prio(FilePosition pos, String value) {
818      super(pos, Collections.<CssTree>emptyList());
819      this.value = value;
820    }
821 
822    @Override
823    public String getValue() { return value; }
824 
825    public void render(RenderContext r) {
826      r.getOut().mark(getFilePosition());
827      r.getOut().consume("!");
828      renderCssIdent(getValue().substring(1).toLowerCase(Locale.ENGLISH), r);
829    }
830  }
831 
832  /**
833   * <pre>
834   * expr
835   *   : term [ operator term ]*
836   * </pre>
837   */
838  public static final class Expr extends CssTree {
839    /** @param novalue ignored but required for reflection. */
840    @ReflectiveCtor
841    public Expr(
842        FilePosition pos, Void novalue, List<? extends CssTree> children) {
843      this(pos, children);
844    }
845 
846    public Expr(FilePosition pos, List<? extends CssTree> children) {
847      super(pos, children);
848    }
849 
850    @Override
851    protected void childrenChanged() {
852      super.childrenChanged();
853      assert 1 == children().size() % 2;
854    }
855 
856    public int getNTerms() { return (children().size() + 1) >> 1; }
857    public Term getNthTerm(int n) { return (Term) children().get(n * 2); }
858    public Operation getNthOperation(int n) {
859      return (Operation) children().get(1 + n * 2);
860    }
861    public Operator getNthOperator(int n) {
862      return ((Operation) children().get(1 + n * 2)).getOperator();
863    }
864 
865    public void render(RenderContext r) {
866      renderSpaceGroup(children(), r);
867    }
868  }
869 
870  /**
871   * <pre>
872   * term
873   *   : unary_operator?
874   *     [ NUMBER S* | PERCENTAGE S* | LENGTH S* | EMS S* | EXS S* | ANGLE S* |
875   *       TIME S* | FREQ S* | function ]
876   *   | STRING S* | IDENT S* | URI S* | RGB S* | UNICODERANGE S* | hexcolor
877   * </pre>
878   */
879  public static final class Term extends CssTree {
880    private final UnaryOperator op;
881    @ReflectiveCtor
882    public Term(FilePosition pos, UnaryOperator op,
883                List<? extends CssExprAtom> oneatom) {
884      this(pos, op, oneatom.get(0));
885    }
886    public Term(FilePosition pos, UnaryOperator op, CssExprAtom expr) {
887      super(pos, Collections.singletonList(expr));
888      this.op = op;
889    }
890    @Override
891    public UnaryOperator getValue() { return op; }
892 
893    public UnaryOperator getOperator() { return op; }
894 
895    public CssExprAtom getExprAtom() { return (CssExprAtom) children().get(0); }
896 
897    public void render(RenderContext r) {
898      r.getOut().mark(getFilePosition());
899      if (null != op) {
900        r.getOut().consume(op.symbol);
901      }
902      getExprAtom().render(r);
903    }
904  }
905 
906  /**
907   * A primitive CSS literal expression or function call. It is an atom in the
908   * sense that cannot be divided into an operator and an operand.
909   * See also http://www.w3.org/TR/REC-CSS2/syndata.html#values
910   */
911  public abstract static class CssExprAtom extends CssTree {
912    CssExprAtom(FilePosition pos, List<? extends CssTree> children) {
913      super(pos, children);
914    }
915    <T extends CssTree>
916    CssExprAtom(
917        FilePosition pos, Class<T> childType, List<? extends T> children) {
918      super(pos, childType, children);
919    }
920  }
921 
922  // these patterns match unescaped values
923  private static final Pattern IDLITERAL = Pattern.compile("^#.+$");
924  private static final Pattern CLASSLITERAL = Pattern.compile("^\\..+$");
925  private static final Pattern IDENTLITERAL = Pattern.compile("^.+$");
926  private static final Pattern HASHLITERAL = Pattern.compile(
927      // The CSS spec allows for 3 and 6 digit forms where "#ABC" is equivalent
928      // to "#AABBCC".  IE filters use a non-standard 8 digit RRGGBB form.
929      "^#[a-fA-F0-9]{3}(?:[a-fA-F0-9]{3}(?:[a-fA-F0-9]{2})?)?$");
930  private static final Pattern QUANTITYLITERAL = Pattern.compile(
931      "^(?:\\.\\d+|\\d+(?:\\.\\d+)?)([a-zA-Z]+|%)?$");
932  private static final Pattern UNICODERANGELITERAL = Pattern.compile(
933      "^U\\+(?:[0-9a-fA-F]{1,6}-[0-9a-fA-F]{1,6}|[0-9a-fA-F?]{1,6})$",
934      Pattern.CASE_INSENSITIVE);
935  private static final Pattern SUBSTITUTION = Pattern.compile(
936      "^\\$\\{.*\\}(?:%|[a-z]+)?$", Pattern.DOTALL);
937 
938  /**
939   * Abstract base class for a literal value such as an ID, CLASS, URI, String,
940   * Color, or keyword value.
941   */
942  public abstract static class CssLiteral extends CssExprAtom {
943    private String value;
944    /**
945     * @param inputValue the unescaped inputValue.  Any unicode escapes have
946     *   been converted to the corresponding character.
947     */
948    CssLiteral(FilePosition pos, String inputValue) {
949      super(pos, Collections.<Expr>emptyList());
950      setValue(inputValue);
951    }
952    @Override
953    public String getValue() { return value; }
954    /**
955     * @param newValue the unescaped value.  Any unicode escapes have been
956     *   converted to the corresponding character.
957     */
958    public void setValue(String newValue) {
959      if (!checkValue(newValue)) {
960        throw new IllegalArgumentException(newValue);
961      }
962      this.value = newValue;
963    }
964    protected abstract boolean checkValue(String value);
965  }
966 
967  /**
968   * An ID in a selector, like {@code #foo}.
969   */
970  public static final class IdLiteral extends CssLiteral {
971    /** @param none ignored but required for reflection. */
972    @ReflectiveCtor
973    public IdLiteral(
974        FilePosition pos, String inputValue, List<? extends CssTree> none) {
975      this(pos, inputValue);
976    }
977    public IdLiteral(FilePosition pos, String value) { super(pos, value); }
978    @Override
979    protected boolean checkValue(String value) {
980      return IDLITERAL.matcher(value).matches();
981    }
982    public void render(RenderContext r) {
983      r.getOut().mark(getFilePosition());
984      r.getOut().consume("#");
985      renderCssIdent(getValue().substring(1), r);
986    }
987  }
988 
989  /**
990   * A class name in a selector like {@code .foo}.
991   */
992  public static final class ClassLiteral extends CssLiteral {
993    /** @param none ignored but required for reflection. */
994    @ReflectiveCtor
995    public ClassLiteral(
996        FilePosition pos, String inputValue, List<? extends CssTree> none) {
997      this(pos, inputValue);
998    }
999    public ClassLiteral(FilePosition pos, String value) { super(pos, value); }
1000    @Override
1001    protected boolean checkValue(String value) {
1002      return CLASSLITERAL.matcher(value).matches();
1003    }
1004    public void render(RenderContext r) {
1005      r.getOut().mark(getFilePosition());
1006      r.getOut().consume(".");
1007      renderCssIdent(getValue().substring(1), r);
1008    }
1009  }
1010 
1011  /**
1012   * A string literal in a property value like {@code 'foo'}.
1013   */
1014  public static final class StringLiteral extends CssLiteral {
1015    /** @param none ignored but required for reflection. */
1016    @ReflectiveCtor
1017    public StringLiteral(
1018        FilePosition pos, String inputValue, List<? extends CssTree> none) {
1019      this(pos, inputValue);
1020    }
1021    public StringLiteral(FilePosition pos, String value) { super(pos, value); }
1022    @Override
1023    protected boolean checkValue(String value) {
1024      return value != null;
1025    }
1026    public void render(RenderContext r) {
1027      r.getOut().mark(getFilePosition());
1028      renderCssString(getValue(), r);
1029    }
1030  }
1031 
1032  /**
1033   * A color value in a property value like {@code #AABBCC}.
1034   */
1035  public static final class HashLiteral extends CssLiteral {
1036    /** @param none ignored but required for reflection. */
1037    @ReflectiveCtor
1038    public HashLiteral(
1039        FilePosition pos, String inputValue, List<? extends CssTree> none) {
1040      this(pos, inputValue);
1041    }
1042    public HashLiteral(FilePosition pos, String value) { super(pos, value); }
1043    public static HashLiteral hex(FilePosition pos, int n, int digits) {
1044      StringBuilder sb = new StringBuilder(digits + 1);
1045      sb.append('#');
1046      while (--digits >= 0) {
1047        sb.append("0123456789abcdef".charAt((n >>> (digits * 4)) & 0xf));
1048      }
1049      return new HashLiteral(pos, sb.toString());
1050    }
1051    @Override
1052    protected boolean checkValue(String value) {
1053      return HASHLITERAL.matcher(value).matches();
1054    }
1055    public void render(RenderContext r) {
1056      r.getOut().mark(getFilePosition());
1057      r.getOut().consume(getValue());
1058    }
1059  }
1060 
1061  /**
1062   * A numeric quantity like {@code 5cm}, {@code 100%}, or {@code 0}.
1063   */
1064  public static final class QuantityLiteral extends CssLiteral {
1065    /** @param none ignored but required for reflection. */
1066    @ReflectiveCtor
1067    public QuantityLiteral(
1068        FilePosition pos, String inputValue, List<? extends CssTree> none) {
1069      this(pos, inputValue);
1070    }
1071    public QuantityLiteral(FilePosition pos, String value) {
1072      super(pos, value);
1073    }
1074    @Override
1075    protected boolean checkValue(String value) {
1076      return QUANTITYLITERAL.matcher(value).matches();
1077    }
1078    public void render(RenderContext r) {
1079      r.getOut().mark(getFilePosition());
1080      r.getOut().consume(getValue());
1081    }
1082  }
1083 
1084  /**
1085   * A range of unicode code-points.
1086   */
1087  public static final class UnicodeRangeLiteral extends CssLiteral {
1088    /** @param none ignored but required for reflection. */
1089    @ReflectiveCtor
1090    public UnicodeRangeLiteral(
1091        FilePosition pos, String inputValue, List<? extends CssTree> none) {
1092      this(pos, inputValue);
1093    }
1094    public UnicodeRangeLiteral(FilePosition pos, String value) {
1095      super(pos, value);
1096    }
1097    @Override
1098    protected boolean checkValue(String value) {
1099      return UNICODERANGELITERAL.matcher(value).matches();
1100    }
1101    public void render(RenderContext r) {
1102      r.getOut().mark(getFilePosition());
1103      r.getOut().consume(getValue());
1104    }
1105  }
1106 
1107  /**
1108   * A uri literal like {@code url('foo/bar.css')}.
1109   */
1110  public static final class UriLiteral extends CssLiteral {
1111    /** @param none ignored but required for reflection. */
1112    @ReflectiveCtor
1113    public UriLiteral(
1114        FilePosition pos, String value, List<? extends CssTree> none) {
1115      this(pos, URI.create(value));
1116    }
1117    public UriLiteral(FilePosition pos, URI value) {
1118      super(pos, value.toString());
1119    }
1120    @Override
1121    protected boolean checkValue(String value) {
1122      try {
1123        URI.create(value);
1124        return true;
1125      } catch (IllegalArgumentException ex) {
1126        return false;
1127      }
1128    }
1129    public void render(RenderContext r) {
1130      TokenConsumer out = r.getOut();
1131      out.mark(getFilePosition());
1132      out.consume("url");
1133      out.consume("(");
1134      String url;
1135      try {
1136        url = UriUtil.normalizeUri(getValue());
1137      } catch (URISyntaxException ex) {
1138        url = "data:,";
1139      }
1140      renderCssString(url, r);
1141      out.mark(FilePosition.endOfOrNull(getFilePosition()));
1142      out.consume(")");
1143    }
1144  }
1145 
1146  /**
1147   * An identifier in a selector like {@code div} or a keyword in a property
1148   * value like {@code auto}.
1149   */
1150  public static final class IdentLiteral extends CssLiteral {
1151    /** @param none ignored but required for reflection. */
1152    @ReflectiveCtor
1153    public IdentLiteral(
1154        FilePosition pos, String value, List<? extends CssTree> none) {
1155      this(pos, value);
1156    }
1157    public IdentLiteral(FilePosition pos, String value) {
1158      super(pos, value);
1159    }
1160    @Override
1161    protected boolean checkValue(String value) {
1162      return IDENTLITERAL.matcher(value).matches();
1163    }
1164    public void render(RenderContext r) {
1165      r.getOut().mark(getFilePosition());
1166      renderCssIdent(getValue(), r);
1167    }
1168  }
1169 
1170  /**
1171   * <pre>
1172   *   function
1173   *   : FUNCTION S* expr ')' S*
1174   * </pre>
1175   */
1176  public static final class FunctionCall extends CssExprAtom {
1177    private final Name name;
1178    @ReflectiveCtor
1179    public FunctionCall(
1180        FilePosition pos, Name name, List<? extends Expr> expr) {
1181      this(pos, name, expr.get(0));
1182    }
1183    public FunctionCall(FilePosition pos, Name name, Expr expr) {
1184      super(pos, Collections.singletonList(expr));
1185      this.name = name;
1186    }
1187    @Override
1188    public Name getValue() { return name; }
1189    public Name getName() { return name; }
1190    public Expr getArguments() { return (Expr) children().get(0); }
1191 
1192    @Override
1193    protected void childrenChanged() {
1194      super.childrenChanged();
1195      assert 1 == children().size() && (children().get(0) instanceof Expr);
1196    }
1197    public void render(RenderContext r) {
1198      TokenConsumer out = r.getOut();
1199      out.mark(getFilePosition());
1200      renderCssIdent(name.getCanonicalForm(), r);
1201      out.consume("(");
1202      children().get(0).render(r);
1203      out.mark(FilePosition.endOfOrNull(getFilePosition()));
1204      out.consume(")");
1205    }
1206  }
1207 
1208  /**
1209   * An IE extension used in the filter property as described at
1210   * http://msdn.microsoft.com/en-us/library/ms532847(VS.85).aspx.
1211   */
1212  public static final class ProgId extends CssExprAtom {
1213    private final Name name;
1214 
1215    @ReflectiveCtor
1216    public ProgId(
1217        FilePosition pos, Name name, List<? extends ProgIdAttribute> attrs) {
1218      super(pos, ProgIdAttribute.class, attrs);
1219      this.name = name;
1220    }
1221 
1222    @Override
1223    public Name getValue() { return name; }
1224    public Name getName() { return name; }
1225    @Override
1226    public List<? extends ProgIdAttribute> children() {
1227      return childrenAs(ProgIdAttribute.class);
1228    }
1229 
1230    public void render(RenderContext r) {
1231      TokenConsumer tc = r.getOut();
1232      tc.mark(getFilePosition());
1233      tc.consume("progid");
1234      tc.consume(":");
1235      boolean dot = false;
1236      for (String part : name.getCanonicalForm().split("\\.")) {
1237        if (dot) { tc.consume("."); }
1238        dot = true;
1239        renderCssIdent(part, r);
1240      }
1241      tc.consume("(");
1242      renderCommaGroup(children(), r);
1243      tc.consume(")");
1244    }
1245  }
1246 
1247  public static final class ProgIdAttribute extends CssTree {
1248    private final Name name;
1249 
1250    @ReflectiveCtor
1251    public ProgIdAttribute(
1252        FilePosition pos, Name name, List<? extends Term> value) {
1253      super(pos, Term.class, value);
1254      this.name = name;
1255    }
1256 
1257    @Override
1258    public Name getValue() { return name; }
1259    public Name getName() { return name; }
1260    @Override
1261    public List<? extends Term> children() {
1262      return childrenAs(Term.class);
1263    }
1264    public Term getPropertyValue() { return children().get(0); }
1265    @Override
1266    protected void childrenChanged() {
1267      super.childrenChanged();
1268      List<? extends Term> terms = children();
1269      if (terms.size() != 1) { throw new IllegalStateException(); }
1270      CssExprAtom atom = terms.get(0).getExprAtom();
1271      if (!(atom instanceof CssLiteral)) {
1272        throw new ClassCastException(atom.getClass().getName());
1273      }
1274    }
1275 
1276    public void render(RenderContext r) {
1277      TokenConsumer tc = r.getOut();
1278      tc.mark(getFilePosition());
1279      renderCssIdent(name.getCanonicalForm(), r);
1280      tc.consume("=");
1281      getPropertyValue().render(r);
1282    }
1283  }
1284 
1285  /**
1286   * A template substitution in a CSS stylesheet.  This is not part of the
1287   * CSS language, and will only be produced if
1288   * {@link com.google.caja.lexer.CssLexer#allowSubstitutions}
1289   * is set.
1290   */
1291  public static final class Substitution extends CssLiteral {
1292    /** @param none ignored but required for reflection. */
1293    public Substitution(
1294        FilePosition pos, String value, List<? extends CssTree> none) {
1295      this(pos, value);
1296    }
1297    public Substitution(FilePosition pos, String value) {
1298      super(pos, value);
1299    }
1300 
1301    public String getBody() {
1302      String value = getValue();
1303      // Produce a string of the same length, so that the file position makes
1304      // sense.
1305      StringBuilder sb = new StringBuilder("  ");  // skip ${
1306      int end = value.lastIndexOf('}');  // until }
1307      sb.append(value, 2, end);
1308      while (sb.length() < value.length()) { sb.append(' '); }
1309      return sb.toString();
1310    }
1311 
1312    public String getSuffix() {
1313      String value = getValue();
1314      return value.substring(value.lastIndexOf('}') + 1);
1315    }
1316 
1317    @Override
1318    public boolean checkValue(String value) {
1319      // TODO(mikesamuel): maybe enforce the convention that there are matched
1320      // parentheses outside C-style strings.
1321      return SUBSTITUTION.matcher(value).matches();
1322    }
1323 
1324    public void render(RenderContext r) {
1325      r.getOut().mark(getFilePosition());
1326      r.getOut().consume(getValue());
1327    }
1328  }
1329 
1330  /** See http://www.w3.org/TR/REC-CSS2/selector.html#q2 */
1331  public static final class Combination extends CssTree {
1332    final Combinator comb;
1333 
1334    /** @param none ignored but required for reflection. */
1335    @ReflectiveCtor
1336    public Combination(
1337        FilePosition pos, Combinator comb, List<? extends CssTree> none) {
1338      this(pos, comb);
1339    }
1340    public Combination(FilePosition pos, Combinator comb) {
1341      super(pos, Collections.<CssTree>emptyList());
1342      this.comb = comb;
1343    }
1344 
1345    @Override
1346    public Combinator getValue() { return comb; }
1347    public Combinator getCombinator() { return comb; }
1348    public void render(RenderContext r) {
1349      r.getOut().mark(getFilePosition());
1350      if (null != comb.symbol) {
1351        r.getOut().consume(comb.symbol);
1352      }
1353    }
1354  }
1355 
1356  /** See http://www.w3.org/TR/REC-CSS2/selector.html#q2 */
1357  public static final class Operation extends CssTree {
1358    final Operator op;
1359 
1360    /** @param none ignored but required for reflection. */
1361    @ReflectiveCtor
1362    public Operation(
1363        FilePosition pos, Operator op, List<? extends CssTree> none) {
1364      this(pos, op);
1365    }
1366    public Operation(FilePosition pos, Operator op) {
1367      super(pos, Collections.<CssTree>emptyList());
1368      this.op = op;
1369    }
1370 
1371    @Override
1372    public Operator getValue() { return op; }
1373 
1374    public Operator getOperator() { return op; }
1375 
1376    public void render(RenderContext r) {
1377      r.getOut().mark(getFilePosition());
1378      if (null != op.symbol) {
1379        r.getOut().consume(op.symbol);
1380      }
1381    }
1382  }
1383 
1384  /**
1385   * <pre>
1386   * operator
1387   *   : '/' S* | ',' S* | <i>empty</i>
1388   * </pre>
1389   */
1390  public static enum Operator {
1391    DIV("/"),
1392    COMMA(","),
1393    EQUAL("="),
1394    NONE(null),
1395    ;
1396    private final String symbol;
1397    Operator(String symbol) { this.symbol = symbol; }
1398    public String getSymbol() { return symbol; }
1399  }
1400 
1401  /**
1402   * <pre>
1403   * unary_operator
1404   *   : '-' | '+'
1405   * </pre>
1406   */
1407  public static enum UnaryOperator {
1408    NEGATION("-"),
1409    IDENTITY("+"),
1410    ;
1411    private final String symbol;
1412    UnaryOperator(String symbol) { this.symbol = symbol; }
1413    public String getSymbol() { return symbol; }
1414  }
1415 
1416  /**
1417   * <pre>
1418   * combinator
1419   *   : '+' S* | '>' S* | <i>empty</i>
1420   * </pre>
1421   */
1422  public static enum Combinator {
1423    SIBLING("+"),
1424    CHILD(">"),
1425    DESCENDANT(null),
1426    ;
1427    private final String symbol;
1428    Combinator(String symbol) { this.symbol = symbol; }
1429    public String getSymbol() { return symbol; }
1430  }
1431 
1432  /**
1433   * A hack that uses syntactically invalid CSS to make a rule visible on some
1434   * user agents but invisible on others.
1435   * <pre>
1436   * user-agent-hack
1437   *   : '*' declaration
1438   * </pre>
1439   */
1440  public static final class UserAgentHack extends Declaration {
1441    private final EnumSet<UserAgent> enabledOn;
1442 
1443    @ReflectiveCtor
1444    public UserAgentHack(
1445        FilePosition pos, Set<UserAgent> enabledOn,
1446        List<? extends PropertyDeclaration> decl) {
1447      super(pos, PropertyDeclaration.class, decl);
1448      this.enabledOn = EnumSet.copyOf(enabledOn);
1449    }
1450 
1451    @Override
1452    public EnumSet<UserAgent> getValue() { return EnumSet.copyOf(enabledOn); }
1453 
1454    @Override
1455    protected void childrenChanged() {
1456      super.childrenChanged();
1457      List<? extends CssTree> children = children();
1458      if (children.size() != 1) { throw new IllegalStateException(); }
1459      if (!(children.get(0) instanceof PropertyDeclaration)) {
1460        throw new ClassCastException(children.get(0).getClass().getName());
1461      }
1462    }
1463 
1464    public PropertyDeclaration getDeclaration() {
1465      return (PropertyDeclaration) children().get(0);
1466    }
1467 
1468    public void render(RenderContext r) {
1469      TokenConsumer out = r.getOut();
1470      out.mark(getFilePosition());
1471      out.consume("*");
1472      getDeclaration().render(r);
1473    }
1474  }
1475 
1476  /** An identifier for a version of a supported browser. */
1477  public static enum UserAgent {
1478    IE6,
1479    IE7,
1480    IE8,
1481    ;
1482 
1483    public static EnumSet<UserAgent> ie7OrOlder() {
1484      return EnumSet.of(IE6, IE7);
1485    }
1486  }
1487 
1488  private static void renderStatements(
1489      List<? extends CssTree> children, FilePosition pos, RenderContext r) {
1490    TokenConsumer out = r.getOut();
1491    out.mark(pos);
1492    out.consume("{");
1493    CssTree last = null;
1494    for (CssTree decl : children) {
1495      if (last != null) {
1496        out.consume(";");
1497      }
1498      decl.render(r);
1499      last = decl;
1500    }
1501    out.mark(FilePosition.endOfOrNull(pos));
1502    out.consume("}");
1503  }
1504 
1505  private static void renderCommaGroup(
1506      List<? extends CssTree> children, RenderContext r) {
1507    boolean first = true;
1508    for (CssTree child : children) {
1509      if (!first) {
1510        r.getOut().consume(",");
1511      } else {
1512        first = false;
1513      }
1514      child.render(r);
1515    }
1516  }
1517 
1518  private static void renderSpaceGroup(
1519      List<? extends CssTree> children, RenderContext r) {
1520    boolean needSpace = false;
1521    for (CssTree child : children) {
1522      if (needSpace) {
1523        r.getOut().consume(" ");
1524      } else {
1525        needSpace = true;
1526      }
1527      child.render(r);
1528    }
1529  }
1530 
1531  private static void renderCssIdent(String ident, RenderContext r) {
1532    StringBuilder sb = new StringBuilder();
1533    Escaping.escapeCssIdent(ident, sb);
1534    r.getOut().consume(sb.toString());
1535  }
1536 
1537  private static void renderCssString(String s, RenderContext r) {
1538    StringBuilder sb = new StringBuilder();
1539    sb.append('\'');
1540    Escaping.escapeCssString(s, sb);
1541    sb.append('\'');
1542    r.getOut().consume(sb.toString());
1543  }
1544}

[all classes][com.google.caja.parser.css]
EMMA 2.0.5312 (C) Vladimir Roubtsov