gt:         for (int i = 0; i < TEST_CONTENT_INVALID.length; i++)
context: package test;
  
  import static org.junit.Assert.assertEquals;
  import static org.junit.Assert.assertNull;
  
  import java.io.IOException;
  import java.util.Date;
  
  import org.junit.BeforeClass;
  import org.junit.Test;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  
  import cz.vutbr.web.css.CSSException;
  import cz.vutbr.web.css.CSSFactory;
  import cz.vutbr.web.css.CSSProperty;
  import cz.vutbr.web.css.Declaration;
  import cz.vutbr.web.css.RuleSet;
  import cz.vutbr.web.css.StyleSheet;
  import cz.vutbr.web.css.TermAngle;
  import cz.vutbr.web.css.TermCalc;
  import cz.vutbr.web.css.TermFactory;
  import cz.vutbr.web.css.TermFloatValue;
  import cz.vutbr.web.css.TermFunction;
  import cz.vutbr.web.css.TermLengthOrPercent;
  import cz.vutbr.web.css.TermNumeric.Unit;
  import cz.vutbr.web.css.TermRect;
  import cz.vutbr.web.css.TermString;
  import cz.vutbr.web.csskit.CalcArgs;
  import cz.vutbr.web.csskit.fn.AttrImpl;
  import cz.vutbr.web.csskit.fn.BlurImpl;
  import cz.vutbr.web.csskit.fn.BrightnessImpl;
  import cz.vutbr.web.csskit.fn.ContrastImpl;
  import cz.vutbr.web.csskit.fn.CounterImpl;
  import cz.vutbr.web.csskit.fn.CountersImpl;
  import cz.vutbr.web.csskit.fn.DropShadowImpl;
  import cz.vutbr.web.csskit.fn.GrayscaleImpl;
  import cz.vutbr.web.csskit.fn.HueRotateImpl;
  import cz.vutbr.web.csskit.fn.InvertImpl;
  import cz.vutbr.web.csskit.fn.LinearGradientImpl;
  import cz.vutbr.web.csskit.fn.Matrix3dImpl;
  import cz.vutbr.web.csskit.fn.MatrixImpl;
  import cz.vutbr.web.csskit.fn.OpacityImpl;
  import cz.vutbr.web.csskit.fn.PerspectiveImpl;
  import cz.vutbr.web.csskit.fn.RadialGradientImpl;
  import cz.vutbr.web.csskit.fn.RepeatingLinearGradientImpl;
  import cz.vutbr.web.csskit.fn.Rotate3dImpl;
  import cz.vutbr.web.csskit.fn.RotateImpl;
  import cz.vutbr.web.csskit.fn.RotateXImpl;
  import cz.vutbr.web.csskit.fn.RotateYImpl;
  import cz.vutbr.web.csskit.fn.RotateZImpl;
  import cz.vutbr.web.csskit.fn.SaturateImpl;
  import cz.vutbr.web.csskit.fn.Scale3dImpl;
  import cz.vutbr.web.csskit.fn.ScaleImpl;
  import cz.vutbr.web.csskit.fn.ScaleXImpl;
  import cz.vutbr.web.csskit.fn.ScaleYImpl;
  import cz.vutbr.web.csskit.fn.ScaleZImpl;
  import cz.vutbr.web.csskit.fn.SepiaImpl;
  import cz.vutbr.web.csskit.fn.SkewImpl;
  import cz.vutbr.web.csskit.fn.SkewXImpl;
  import cz.vutbr.web.csskit.fn.SkewYImpl;
  import cz.vutbr.web.csskit.fn.Translate3dImpl;
  import cz.vutbr.web.csskit.fn.TranslateImpl;
  import cz.vutbr.web.csskit.fn.TranslateXImpl;
  import cz.vutbr.web.csskit.fn.TranslateYImpl;
  import cz.vutbr.web.csskit.fn.TranslateZImpl;
  
  public class FunctionsTest {
  	private static final Logger log = LoggerFactory.getLogger(FunctionsTest.class);
  
  	public static final TermFactory tf = CSSFactory.getTermFactory();
  
  	/* parentheses in strings - pair matching test */
  	public static final String TEST_DECL1A = " a:after { content: \"(\" attr(href) \")\"; border: 1px solid red; }";
      public static final String TEST_DECL1B = " a:after { content: \"{\" attr(href) \"}\"; border: 1px solid red; }";
  
      /* function name may start with minus */
      public static final String TEST_DECL2A = " a:after { background-image:-moz-linear-gradient(top,#fff,#ececec); border: 1px solid red; }";
      
      /* function arguments may include minus */
      public static final String TEST_DECL3A = "img { translate: translateY(-.1em) }";
      
      /* different rect() syntaxes */
      public static final String TEST_RECT1 = "p { clip: rect(1px 10em 3rem 2ch); color: red; }";
      public static final String TEST_RECT2 = "p { clip: rect(1px, 10em, 3rem, 2ch); color: red; }";
      public static final String TEST_RECT3 = "p { clip: rect(1px, 10em, 3rem, auto); color: red; }";
      
      /* calc() length expressions (all should evaluate to 60.0) */
      public static final String TEST_CALC_L[] = new String[] {
          "p { width: calc(60px); color: red; }",
          "p { width: calc(1em + 0.5em); color: red; }",
          "p { width: calc(1em + (10% * 2)); color: red; }",
          "p { width: calc(-3em + 4.5em); color: red; }",
          "p { width: calc(3em + (-1.5em)); color: red; }",
          "p { width: calc(3em - 1.5em); color: red; }"
      };
      
      /* calc() angle expressions (all should evaluate to 33) */
      public static final String TEST_CALC_A[];
      static {
          TEST_CALC_A = new String[1];
          TEST_CALC_A[0] = "p { azimuth: calc(30deg + 6rad); color: red; }";
      }
      
      /* invalid calc() expressions (all width declarations should be skipped) */
      public static final String TEST_CALC_I[];
      static {
          TEST_CALC_I = new String[1];
          TEST_CALC_I[0] = "p { width: calc(30deg + 3em * 2); color: red; }"; //mixed types in expression
      }
  
      /* valid transform functions */
      public static final String TEST_TRANSFORM[] = new String[] {
          "p { transform: matrix(1, 2, -1, 1.2, 80, 80); }", 
          "p { transform: matrix3d(1.1, 2.2, 0, 4.4, 5.5, -6.6, 7.7, 8.8, 9, 10, -11, 12, 13.0, 14, 15, 16); }", 
          "p { transform: perspective(800px); }", 
          "p { transform: perspective(0); }", 
          "p { transform: rotate(45deg); }", 
          "p { transform: rotate(0); }", 
          "p { transform: rotate3d(1, 1, 1, 45deg); }", 
          "p { transform: rotate3d(2, -1, -1, -0.2turn); }", 
          "p { transform: rotateX(-0.2turn); }", 
          "p { transform: rotateX(0); }", 
          "p { transform: rotateY(-0.2turn); }", 
          "p { transform: rotateY(0); }", 
          "p { transform: rotateZ(-0.2turn); }", 
          "p { transform: rotateZ(0); }", 
          "p { transform: scale(1.5, 1.2); }", 
          "p { transform: scale(1.3); }", 
          "p { transform: scale3d(-0.5, 1, 1.7); }", 
          "p { transform: scaleX(1.5); }", 
          "p { transform: scaleY(-1.5); }", 
          "p { transform: scaleZ(1); }", 
          "p { transform: skew(0); }", 
          "p { transform: skew(15deg, 15deg); }", 
          "p { transform: skew(.312rad); }", 
          "p { transform: skewX(-35deg); }", 
          "p { transform: skewY(0); }", 
          "p { transform: translate(10px); }", 
          "p { transform: translate(10px, 25%); }", 
          "p { transform: translate(0, 12pt); }", 
          "p { transform: translate3d(5ch, -0.4in, 5em); }", 
          "p { transform: translateX(10px); }", 
          "p { transform: translateY(-1.5em); }", 
          "p { transform: translateZ(0); }" 
      };
      
      /* invalid transform functions; only the float declaration should be accepted */
      public static final String TEST_TRANSFORM_INVALID[] = new String[] {
          "p { float: left; transform: scale(1.5, 1.2, 1.1); }",
          "p { float: left; transform: scale(); }"
      };
      
      /* valid gradient functions */
      public static final String TEST_GRADIENT[] = new String[] {
          "p { background: linear-gradient(#e66465, #9198e5); }",
          "p { background: linear-gradient(0.25turn, red, #ebf8e1, #f69d3c); }",
          "p { background: linear-gradient(to left, #333, #333 50%, #eee 75%, #333 75%); }",
          "p { background: linear-gradient(to bottom right, #333, #333 50%, #eee); }",
          "p { background: repeating-linear-gradient(#e66465, #9198e5); }",
          "p { background: repeating-linear-gradient(0.25turn, red, #ebf8e1, #f69d3c); }",
          "p { background: repeating-linear-gradient(to left, #333, #333 50%, #eee 75%, #333 75%); }",
          "p { background: repeating-linear-gradient(to bottom right, #333, #333 50%, #eee); }",
          "p { background: radial-gradient(cyan 0%, transparent 20%, salmon 40%); }",
          "p { background: radial-gradient(farthest-corner at 30px 40px, #f35 0%, #43e 100%); }",
          "p { background: radial-gradient(#e66465, #9198e5); }",
          "p { background: radial-gradient(closest-side, #3f87a6, #ebf8e1, #f69d3c); }",
          "p { background: radial-gradient(circle at 100%, #333, #333 50%, #eee 75%, #333 75%); }",
          "p { background: radial-gradient(circle 10px at 100%, #333, #eee); }",
          "p { background: radial-gradient(ellipse 10px 55% at 10px 20px, #333, #eee); }",
          "p { background: radial-gradient(10px at center, #333, #eee); }",
          "p { background: radial-gradient(10% 12% at center, #333, #eee); }",
          "p { background: radial-gradient(ellipse at top, #e66465, transparent), radial-gradient(ellipse at bottom, #4d9f0c, transparent); }",
      };
      
      /* invalid gradient functions; only the float declaration should be accepted */
      public static final String TEST_GRADIENT_INVALID[] = new String[] {
          "p { float: left; background: linear-gradient(12pt, red); }",
          "p { float: left; background: linear-gradient(to top bottom, red, blue); }",
          "p { float: left; background: linear-gradient(top left, red, blue); }",
          "p { float: left; background: linear-gradient(to nowhere, red, blue); }",
          "p { float: left; background: radial-gradient(circle 10% at 100%, #333, #eee); }",
          "p { float: left; background: radial-gradient(circle 10px 20px at 100%, #333, #eee); }",
          "p { float: left; background: radial-gradient(ellipse 10px at 10px 20px, #333, #eee); }",
          "p { float: left; background: radial-gradient(ellipse at 10px 20px 30px, #333, #eee); }",
          "p { float: left; background: radial-gradient(ellipse unknown at top bottom, #333, #eee); }"
      };
      
      /* valid filter functions */
      public static final String TEST_FILTER[] = new String[] {
          "p { filter: blur(1.2em); }",
          "p { filter: brightness(1.75); }",
          "p { filter: contrast(200%); }",
          "p { filter: drop-shadow(30px 10px 4px #4444dd); }",
          "p { filter: drop-shadow(-30px -10px red); }",
          "p { filter: grayscale(1); }",
          "p { filter: hue-rotate(-3.142rad); }",
          "p { filter: invert(0.30); }",
          "p { filter: opacity(0); }",
          "p { filter: saturate(50%); }",
          "p { filter: sepia(1); }",
      };
      
      /* valid content functions */
      public static final String TEST_CONTENT[] = new String[] {
          "p:before { content: attr(title); }",
          "p:before { content: counter(chapter_counter); }",
          "p:before { content: counter(chapter_counter, lower-alpha); }",
          "p:before { content: counters(chapter_counter, '..'); }",
          "p:before { content: counters(chapter_counter, '::', lower-roman); }",
          "p:before { content: counter(c, none) \"z\"; }"
      };
      
      /* invalid content functions */
      public static final String TEST_CONTENT_INVALID[] = new String[] {
          "p:before { font-weight: bold; content: counter(chapter_counter, '.'); }",
          "p:before { font-weight: bold; content: counter(chapter_counter, weird); }",
          "p:before { font-weight: bold; content: counters(chapter_counter); }",
          "p:before { font-weight: bold; content: counters(chapter_counter, '::', unknown); }",
      };
      
  	@BeforeClass
  	public static void init() 
  	{
  		log.info("\n\n\n == Functions test at {} == \n\n\n", new Date());
  	}
  
  	@Test
  	public void vendorSpecificUnderscore() throws IOException, CSSException 
  	{
  		StyleSheet ss = CSSFactory.parseString(TEST_DECL1A, null);
  		assertEquals("Two properties are accepted", 2, ss.get(0).size());
  		
  		final RuleSet rule = (RuleSet) ss.get(0);
          assertEquals("The first property value has three terms", 3, rule.get(0).size());
  	}
  	
  	@Test
  	public void vendorSpecificFunctions() throws IOException, CSSException 
  	{
  		StyleSheet ss = CSSFactory.parseString(TEST_DECL2A, null);
  		assertEquals("Two properties are accepted", 2, ss.get(0).size());
  		Declaration d = (Declaration) ss.get(0).get(0);
  		TermFunction f = (TermFunction) d.get(0);
  		char first = f.getFunctionName().charAt(0);
  		assertEquals("Function name starts with minus", '-', first);
  	}
      
      @Test
  	public void negativeArgument() throws IOException, CSSException 
  	{
  		StyleSheet ss = CSSFactory.parseString(TEST_DECL3A, null);
  		assertEquals("One properties is accepted", 1, ss.get(0).size());
  		Declaration d = (Declaration) ss.get(0).get(0);
  		TermFunction f = (TermFunction) d.get(0);
  		assertEquals("The argument is -0.1em", tf.createLength(-0.1f, Unit.em), f.get(0));
  	}
  	
  	@Test
  	public void rectFunctions() throws IOException, CSSException
  	{
          StyleSheet ss1 = CSSFactory.parseString(TEST_RECT1, null);
          assertEquals("Two properties are accepted", 2, ss1.get(0).size());
          Declaration d1 = (Declaration) ss1.get(0).get(0);
          TermRect r1 = (TermRect) d1.get(0);
          assertEquals("The last one is a correct length", tf.createLength(2f, Unit.ch), r1.getValue().get(3));
  	    
          StyleSheet ss2 = CSSFactory.parseString(TEST_RECT2, null);
          assertEquals("Two properties are accepted", 2, ss2.get(0).size());
          Declaration d2 = (Declaration) ss2.get(0).get(0);
          TermRect r2 = (TermRect) d2.get(0);
          assertEquals("The last one is a correct length", tf.createLength(2f, Unit.ch), r2.getValue().get(3));
          
          StyleSheet ss3 = CSSFactory.parseString(TEST_RECT3, null);
          assertEquals("Two properties are accepted", 2, ss3.get(0).size());
          Declaration d3 = (Declaration) ss3.get(0).get(0);
          TermRect r3 = (TermRect) d3.get(0);
          assertEquals("The last one is a correct length", null, r3.getValue().get(3));
  	}
  	
  	@Test
      public void calcLengths() throws IOException, CSSException
      {
  	    PxEvaluator eval = new PxEvaluator();
  	    for (int i = 0; i < TEST_CALC_L.length; i++)
  	    {
  	        StyleSheet ss = CSSFactory.parseString(TEST_CALC_L[i], null);
  	        assertEquals("Two properties are accepted [" + i + "]", 2, ss.get(0).size());
  	        Declaration d = (Declaration) ss.get(0).get(0);
  	        TermCalc calc = (TermCalc) d.get(0);
  	        Double result = calc.getArgs().evaluate(eval);
              assertEquals("Experssion result is correct [" + i + "]", 60.0, result.doubleValue(), 0.000001);
  	    }
      }
  	
      @Test
      public void calcAngles() throws IOException, CSSException
      {
          DegEvaluator eval = new DegEvaluator();
          for (int i = 0; i < TEST_CALC_A.length; i++)
          {
              StyleSheet ss = CSSFactory.parseString(TEST_CALC_A[i], null);
              assertEquals("Two properties are accepted [" + i + "]", 2, ss.get(0).size());
              Declaration d = (Declaration) ss.get(0).get(0);
              TermCalc calc = (TermCalc) d.get(0);
              Double result = calc.getArgs().evaluate(eval);
              assertEquals("Experssion result is correct [" + i + "]", 33.0, result.doubleValue(), 0.000001);
          }
      }
      
      @Test
      public void calcInvalid() throws IOException, CSSException
      {
          for (int i = 0; i < TEST_CALC_I.length; i++)
          {
              StyleSheet ss = CSSFactory.parseString(TEST_CALC_I[i], null);
              assertEquals("Only one property is accepted [" + i + "]", 1, ss.get(0).size());
          }
      }
      
      @Test
      public void transformValid() throws IOException, CSSException
      {
          for (int i = 0; i < TEST_TRANSFORM.length; i++)
          {
              StyleSheet ss = CSSFactory.parseString(TEST_TRANSFORM[i], null);
              //System.out.println(i + " ss: " + ss);
              assertEquals("One rule is parset [" + i + "]", 1, ss.size());
              assertEquals("One property is set [" + i + "]", 1, ss.get(0).size());
              Declaration d = (Declaration) ss.get(0).get(0);
              TermFunction fn = (TermFunction) d.get(0);
              //System.out.println(i + ": " + d);
              switch (i)
              {
                  case 0:
                      assertEquals(MatrixImpl.class, fn.getClass());
                      break;
                  case 1:
                      assertEquals(Matrix3dImpl.class, fn.getClass());
                      break;
                  case 2:
                      assertEquals(PerspectiveImpl.class, fn.getClass());
                      break;
                  case 3:
                      assertEquals(PerspectiveImpl.class, fn.getClass());
                      break;
                  case 4:
                      assertEquals(RotateImpl.class, fn.getClass());
                      assertEquals("Angle is correct", tf.createAngle("45", Unit.deg, 1), ((RotateImpl) fn).getAngle());
                      break;
                  case 5:
                      assertEquals(RotateImpl.class, fn.getClass());
                      assertEquals("Angle is correct", tf.createAngle(0.0f), ((RotateImpl) fn).getAngle());
                      break;
                  case 6:
                      assertEquals(Rotate3dImpl.class, fn.getClass());
                      break;
                  case 7:
                      assertEquals(Rotate3dImpl.class, fn.getClass());
                      break;
                  case 8:
                      assertEquals(RotateXImpl.class, fn.getClass());
                      break;
                  case 9:
                      assertEquals(RotateXImpl.class, fn.getClass());
                      break;
                  case 10:
                      assertEquals(RotateYImpl.class, fn.getClass());
                      break;
                  case 11:
                      assertEquals(RotateYImpl.class, fn.getClass());
                      break;
                  case 12:
                      assertEquals(RotateZImpl.class, fn.getClass());
                      break;
                  case 13:
                      assertEquals(RotateZImpl.class, fn.getClass());
                      break;
                  case 14:
                      assertEquals(ScaleImpl.class, fn.getClass());
                      assertEquals("ScaleX is correct", 1.5f, ((ScaleImpl) fn).getScaleX(), 0.001f);
                      assertEquals("ScaleY is correct", 1.2f, ((ScaleImpl) fn).getScaleY(), 0.001f);
                      break;
                  case 15:
                      assertEquals(ScaleImpl.class, fn.getClass());
                      assertEquals("ScaleX is correct", 1.3f, ((ScaleImpl) fn).getScaleX(), 0.001f);
                      assertEquals("ScaleY is correct", 1.3f, ((ScaleImpl) fn).getScaleY(), 0.001f);
                      break;
                  case 16:
                      assertEquals(Scale3dImpl.class, fn.getClass());
                      assertEquals("ScaleX is correct", -0.5f, ((Scale3dImpl) fn).getScaleX(), 0.001f);
                      assertEquals("ScaleY is correct", 1f, ((Scale3dImpl) fn).getScaleY(), 0.001f);
                      assertEquals("ScaleZ is correct", 1.7f, ((Scale3dImpl) fn).getScaleZ(), 0.001f);
                      break;
                  case 17:
                      assertEquals(ScaleXImpl.class, fn.getClass());
                      assertEquals("Scale is correct", 1.5f, ((ScaleXImpl) fn).getScale(), 0.001f);
                      break;
                  case 18:
                      assertEquals(ScaleYImpl.class, fn.getClass());
                      assertEquals("Scale is correct", -1.5f, ((ScaleYImpl) fn).getScale(), 0.001f);
                      break;
                  case 19:
                      assertEquals(ScaleZImpl.class, fn.getClass());
                      assertEquals("Scale is correct", 1f, ((ScaleZImpl) fn).getScale(), 0.001f);
                      break;
                  case 20:
                      assertEquals(SkewImpl.class, fn.getClass());
                      break;
                  case 21:
                      assertEquals(SkewImpl.class, fn.getClass());
                      break;
                  case 22:
                      assertEquals(SkewImpl.class, fn.getClass());
                      break;
                  case 23:
                      assertEquals(SkewXImpl.class, fn.getClass());
                      break;
                  case 24:
                      assertEquals(SkewYImpl.class, fn.getClass());
                      break;
                  case 25:
                      assertEquals(TranslateImpl.class, fn.getClass());
                      assertEquals("Length X is correct", tf.createLength("10", Unit.px, 1), ((TranslateImpl) fn).getTranslateX());
                      assertEquals("Length Y is correct", tf.createLength(0.0f), ((TranslateImpl) fn).getTranslateY());
                      break;
                  case 26:
                      assertEquals(TranslateImpl.class, fn.getClass());
                      assertEquals("Length X is correct", tf.createLength("10", Unit.px, 1), ((TranslateImpl) fn).getTranslateX());
                      assertEquals("Length Y is correct", tf.createPercent(25.0f), ((TranslateImpl) fn).getTranslateY());
                      break;
                  case 27:
                      assertEquals(TranslateImpl.class, fn.getClass());
                      assertEquals("Length X is correct", tf.createInteger(0), ((TranslateImpl) fn).getTranslateX());
                      assertEquals("Length Y is correct", tf.createLength("12", Unit.pt, 1), ((TranslateImpl) fn).getTranslateY());
                      break;
                  case 28:
                      assertEquals(Translate3dImpl.class, fn.getClass());
                      break;
                  case 29:
                      assertEquals(TranslateXImpl.class, fn.getClass());
                      break;
                  case 30:
                      assertEquals(TranslateYImpl.class, fn.getClass());
                      break;
                  case 31:
                      assertEquals(TranslateZImpl.class, fn.getClass());
                      break;
              }
          }
      }
      
      @Test
      public void transformInvalid() throws IOException, CSSException
      {
          for (int i = 0; i < TEST_TRANSFORM_INVALID.length; i++)
          {
              StyleSheet ss = CSSFactory.parseString(TEST_TRANSFORM_INVALID[i], null);
              assertEquals("One rule is parset [" + i + "]", 1, ss.size());
              assertEquals("One property is set [" + i + "]", 1, ss.get(0).size());
          }        
      }
      
      @Test
      public void gradientValid() throws IOException, CSSException
      {
          for (int i = 0; i < TEST_GRADIENT.length; i++)
          {
              StyleSheet ss = CSSFactory.parseString(TEST_GRADIENT[i], null);
              //System.out.println(i + " ss: " + ss);
              assertEquals("One rule is parsed [" + i + "]", 1, ss.size());
              assertEquals("One property is set [" + i + "]", 1, ss.get(0).size());
              Declaration d = (Declaration) ss.get(0).get(0);
              TermFunction fn = (TermFunction) d.get(0);
              //System.out.println(i + ": " + d);
              switch (i)
              {
                  case 0:
                      assertEquals(LinearGradientImpl.class, fn.getClass());
                      assertNull("Angle is not set", ((LinearGradientImpl) fn).getAngle());
                      assertEquals("Two stops are set", 2, ((LinearGradientImpl) fn).getColorStops().size());
                      assertEquals("First stop color", tf.createColor("#e66465"), ((LinearGradientImpl) fn).getColorStops().get(0).getColor());
                      assertNull("First stop length", ((LinearGradientImpl) fn).getColorStops().get(0).getLength());
                      assertEquals("Second stop color", tf.createColor("#9198e5"), ((LinearGradientImpl) fn).getColorStops().get(1).getColor());
                      assertNull("Second stop length", ((LinearGradientImpl) fn).getColorStops().get(1).getLength());
                      break;
                  case 1:
                      assertEquals(LinearGradientImpl.class, fn.getClass());
                      assertEquals("Angle", tf.createAngle("0.25", Unit.turn, 1), ((LinearGradientImpl) fn).getAngle());
                      assertEquals("Three stops are set", 3, ((LinearGradientImpl) fn).getColorStops().size());
                      assertEquals("First stop color", tf.createColor("#ff0000"), ((LinearGradientImpl) fn).getColorStops().get(0).getColor());
                      assertNull("First stop length", ((LinearGradientImpl) fn).getColorStops().get(0).getLength());
                      assertEquals("Last stop color", tf.createColor("#f69d3c"), ((LinearGradientImpl) fn).getColorStops().get(2).getColor());
                      assertNull("Last stop length", ((LinearGradientImpl) fn).getColorStops().get(2).getLength());
                      break;
                  case 2:
                      assertEquals(LinearGradientImpl.class, fn.getClass());
                      assertEquals("Angle", tf.createAngle("270", Unit.deg, 1), ((LinearGradientImpl) fn).getAngle());
                      assertEquals("Four stops are set", 4, ((LinearGradientImpl) fn).getColorStops().size());
                      assertEquals("First stop color", tf.createColor("#333333"), ((LinearGradientImpl) fn).getColorStops().get(0).getColor());
                      assertNull("First stop length", ((LinearGradientImpl) fn).getColorStops().get(0).getLength());
                      assertEquals("Last stop color", tf.createColor("#333333"), ((LinearGradientImpl) fn).getColorStops().get(3).getColor());
                      assertEquals("Last stop length", tf.createPercent(75.0f), ((LinearGradientImpl) fn).getColorStops().get(3).getLength());
                      break;
                  case 3:
                      assertEquals(LinearGradientImpl.class, fn.getClass());
                      assertEquals("Angle", tf.createAngle("135", Unit.deg, 1), ((LinearGradientImpl) fn).getAngle());
                      assertEquals("Three stops are set", 3, ((LinearGradientImpl) fn).getColorStops().size());
                      assertEquals("First stop color", tf.createColor("#333333"), ((LinearGradientImpl) fn).getColorStops().get(0).getColor());
                      assertNull("First stop length", ((LinearGradientImpl) fn).getColorStops().get(0).getLength());
                      assertEquals("Second stop color", tf.createColor("#333333"), ((LinearGradientImpl) fn).getColorStops().get(1).getColor());
                      assertEquals("Second stop length", tf.createPercent(50.0f), ((LinearGradientImpl) fn).getColorStops().get(1).getLength());
                      break;
                  case 4:
                      assertEquals(RepeatingLinearGradientImpl.class, fn.getClass());
                      assertNull("Angle is not set", ((RepeatingLinearGradientImpl) fn).getAngle());
                      assertEquals("Two stops are set", 2, ((RepeatingLinearGradientImpl) fn).getColorStops().size());
                      assertEquals("First stop color", tf.createColor("#e66465"), ((RepeatingLinearGradientImpl) fn).getColorStops().get(0).getColor());
                      assertNull("First stop length", ((RepeatingLinearGradientImpl) fn).getColorStops().get(0).getLength());
                      assertEquals("Second stop color", tf.createColor("#9198e5"), ((RepeatingLinearGradientImpl) fn).getColorStops().get(1).getColor());
                      assertNull("Second stop length", ((RepeatingLinearGradientImpl) fn).getColorStops().get(1).getLength());
                      break;
                  case 5:
                      assertEquals(RepeatingLinearGradientImpl.class, fn.getClass());
                      assertEquals("Angle", tf.createAngle("0.25", Unit.turn, 1), ((RepeatingLinearGradientImpl) fn).getAngle());
                      assertEquals("Three stops are set", 3, ((RepeatingLinearGradientImpl) fn).getColorStops().size());
                      assertEquals("First stop color", tf.createColor("#ff0000"), ((RepeatingLinearGradientImpl) fn).getColorStops().get(0).getColor());
                      assertNull("First stop length", ((RepeatingLinearGradientImpl) fn).getColorStops().get(0).getLength());
                      assertEquals("Last stop color", tf.createColor("#f69d3c"), ((RepeatingLinearGradientImpl) fn).getColorStops().get(2).getColor());
                      assertNull("Last stop length", ((RepeatingLinearGradientImpl) fn).getColorStops().get(2).getLength());
                      break;
                  case 6:
                      assertEquals(RepeatingLinearGradientImpl.class, fn.getClass());
                      assertEquals("Angle", tf.createAngle("270", Unit.deg, 1), ((RepeatingLinearGradientImpl) fn).getAngle());
                      assertEquals("Four stops are set", 4, ((RepeatingLinearGradientImpl) fn).getColorStops().size());
                      assertEquals("First stop color", tf.createColor("#333333"), ((RepeatingLinearGradientImpl) fn).getColorStops().get(0).getColor());
                      assertNull("First stop length", ((RepeatingLinearGradientImpl) fn).getColorStops().get(0).getLength());
                      assertEquals("Last stop color", tf.createColor("#333333"), ((RepeatingLinearGradientImpl) fn).getColorStops().get(3).getColor());
                      assertEquals("Last stop length", tf.createPercent(75.0f), ((RepeatingLinearGradientImpl) fn).getColorStops().get(3).getLength());
                      break;
                  case 7:
                      assertEquals(RepeatingLinearGradientImpl.class, fn.getClass());
                      assertEquals("Angle", tf.createAngle("135", Unit.deg, 1), ((RepeatingLinearGradientImpl) fn).getAngle());
                      assertEquals("Three stops are set", 3, ((RepeatingLinearGradientImpl) fn).getColorStops().size());
                      assertEquals("First stop color", tf.createColor("#333333"), ((RepeatingLinearGradientImpl) fn).getColorStops().get(0).getColor());
                      assertNull("First stop length", ((RepeatingLinearGradientImpl) fn).getColorStops().get(0).getLength());
                      assertEquals("Second stop color", tf.createColor("#333333"), ((RepeatingLinearGradientImpl) fn).getColorStops().get(1).getColor());
                      assertEquals("Second stop length", tf.createPercent(50.0f), ((RepeatingLinearGradientImpl) fn).getColorStops().get(1).getLength());
                      break;
                  case 8:
                      assertEquals(RadialGradientImpl.class, fn.getClass());
                      assertNull("No size", ((RadialGradientImpl) fn).getSize());
                      assertEquals("Size ident", tf.createIdent("farthest-corner"), ((RadialGradientImpl) fn).getSizeIdent());
                      assertEquals("Shape is ellipse", tf.createIdent("ellipse"), ((RadialGradientImpl) fn).getShape());
                      assertEquals("Default position", 2, ((RadialGradientImpl) fn).getPosition().length);
                      assertEquals("Three stops are set", 3, ((RadialGradientImpl) fn).getColorStops().size());
                      assertEquals("First stop color", tf.createColor("#00ffff"), ((RadialGradientImpl) fn).getColorStops().get(0).getColor());
                      assertEquals("First stop length", tf.createPercent(0f), ((RadialGradientImpl) fn).getColorStops().get(0).getLength());
                      assertEquals("Second stop color", tf.createColor(tf.createIdent("transparent")), ((RadialGradientImpl) fn).getColorStops().get(1).getColor());
                      assertEquals("Second stop length", tf.createPercent(20.0f), ((RadialGradientImpl) fn).getColorStops().get(1).getLength());
                      break;
                  case 9:
                      assertEquals(RadialGradientImpl.class, fn.getClass());
                      assertNull("No size", ((RadialGradientImpl) fn).getSize());
                      assertEquals("Size ident", tf.createIdent("farthest-corner"), ((RadialGradientImpl) fn).getSizeIdent());
                      assertEquals("Shape is ellipse", tf.createIdent("ellipse"), ((RadialGradientImpl) fn).getShape());
                      assertEquals("Position is set", 2, ((RadialGradientImpl) fn).getPosition().length);
                      assertEquals("Position 1 is correct", tf.createLength(30.0f, Unit.px), ((RadialGradientImpl) fn).getPosition()[0]);
                      assertEquals("Position 2 is correct", tf.createLength(40.0f, Unit.px), ((RadialGradientImpl) fn).getPosition()[1]);
                      assertEquals("Two stops are set", 2, ((RadialGradientImpl) fn).getColorStops().size());
                      break;
                  case 10:
                      assertEquals(RadialGradientImpl.class, fn.getClass());
                      assertNull("No size", ((RadialGradientImpl) fn).getSize());
                      assertEquals("Size ident", tf.createIdent("farthest-corner"), ((RadialGradientImpl) fn).getSizeIdent());
                      assertEquals("Shape is ellipse", tf.createIdent("ellipse"), ((RadialGradientImpl) fn).getShape());
                      assertEquals("Default position", 2, ((RadialGradientImpl) fn).getPosition().length);
                      assertEquals("Two stops are set", 2, ((RadialGradientImpl) fn).getColorStops().size());
                      assertEquals("First stop color", tf.createColor("#e66465"), ((RadialGradientImpl) fn).getColorStops().get(0).getColor());
                      assertNull("First stop length", ((RadialGradientImpl) fn).getColorStops().get(0).getLength());
                      assertEquals("Second stop color", tf.createColor("#9198e5"), ((RadialGradientImpl) fn).getColorStops().get(1).getColor());
                      assertNull("Second stop length", ((RadialGradientImpl) fn).getColorStops().get(1).getLength());
                      break;
                  case 11:
                      assertEquals(RadialGradientImpl.class, fn.getClass());
                      assertNull("No size", ((RadialGradientImpl) fn).getSize());
                      assertEquals("Size ident", tf.createIdent("closest-side"), ((RadialGradientImpl) fn).getSizeIdent());
                      assertEquals("Shape is ellipse", tf.createIdent("ellipse"), ((RadialGradientImpl) fn).getShape());
                      assertEquals("Position is set", 2, ((RadialGradientImpl) fn).getPosition().length);
                      assertEquals("Position 1 is correct", tf.createPercent(50.0f), ((RadialGradientImpl) fn).getPosition()[0]);
                      assertEquals("Position 2 is correct", tf.createPercent(50.0f), ((RadialGradientImpl) fn).getPosition()[1]);
                      assertEquals("Three stops are set", 3, ((RadialGradientImpl) fn).getColorStops().size());
                      break;
                  case 12:
                      assertEquals(RadialGradientImpl.class, fn.getClass());
                      assertNull("No size", ((RadialGradientImpl) fn).getSize());
                      assertEquals("Shape is circle", tf.createIdent("circle"), ((RadialGradientImpl) fn).getShape());
                      assertEquals("Position is set", 2, ((RadialGradientImpl) fn).getPosition().length);
                      assertEquals("Position 1 is correct", tf.createPercent(100.0f), ((RadialGradientImpl) fn).getPosition()[0]);
                      assertEquals("Position 2 is correct", tf.createPercent(50.0f), ((RadialGradientImpl) fn).getPosition()[1]);
                      assertEquals("Four stops are set", 4, ((RadialGradientImpl) fn).getColorStops().size());
                      break;
                  case 13:
                      assertEquals(RadialGradientImpl.class, fn.getClass());
                      assertEquals("Single size", 1, ((RadialGradientImpl) fn).getSize().length);
                      assertEquals("Correct size", tf.createLength(10.0f, Unit.px), ((RadialGradientImpl) fn).getSize()[0]);
                      assertEquals("Shape is circle", tf.createIdent("circle"), ((RadialGradientImpl) fn).getShape());
                      assertEquals("Position is set", 2, ((RadialGradientImpl) fn).getPosition().length);
                      assertEquals("Position 1 is correct", tf.createPercent(100.0f), ((RadialGradientImpl) fn).getPosition()[0]);
                      assertEquals("Position 2 is correct", tf.createPercent(50.0f), ((RadialGradientImpl) fn).getPosition()[1]);
                      assertEquals("Two stops are set", 2, ((RadialGradientImpl) fn).getColorStops().size());
                      break;
                  case 14:
                      assertEquals(RadialGradientImpl.class, fn.getClass());
                      assertEquals("Two sizes", 2, ((RadialGradientImpl) fn).getSize().length);
                      assertEquals("Correct size 1", tf.createLength(10.0f, Unit.px), ((RadialGradientImpl) fn).getSize()[0]);
                      assertEquals("Correct size 2", tf.createPercent(55.0f), ((RadialGradientImpl) fn).getSize()[1]);
                      assertEquals("Shape is ellipse", tf.createIdent("ellipse"), ((RadialGradientImpl) fn).getShape());
                      assertEquals("Position is set", 2, ((RadialGradientImpl) fn).getPosition().length);
                      assertEquals("Position 1 is correct", tf.createLength(10.0f, Unit.px), ((RadialGradientImpl) fn).getPosition()[0]);
                      assertEquals("Position 2 is correct", tf.createLength(20.0f, Unit.px), ((RadialGradientImpl) fn).getPosition()[1]);
                      assertEquals("Two stops are set", 2, ((RadialGradientImpl) fn).getColorStops().size());
                      break;
                  case 15:
                      assertEquals(RadialGradientImpl.class, fn.getClass());
                      assertEquals("Single size", 1, ((RadialGradientImpl) fn).getSize().length);
                      assertEquals("Correct size", tf.createLength(10.0f, Unit.px), ((RadialGradientImpl) fn).getSize()[0]);
                      assertEquals("Shape is circle", tf.createIdent("circle"), ((RadialGradientImpl) fn).getShape());
                      assertEquals("Position is set", 2, ((RadialGradientImpl) fn).getPosition().length);
                      assertEquals("Position 1 is correct", tf.createPercent(50.0f), ((RadialGradientImpl) fn).getPosition()[0]);
                      assertEquals("Position 2 is correct", tf.createPercent(50.0f), ((RadialGradientImpl) fn).getPosition()[1]);
                      assertEquals("Two stops are set", 2, ((RadialGradientImpl) fn).getColorStops().size());
                      break;
                  case 16:
                      assertEquals(RadialGradientImpl.class, fn.getClass());
                      assertEquals("Two sizes", 2, ((RadialGradientImpl) fn).getSize().length);
                      assertEquals("Correct size 1", tf.createPercent(10.0f), ((RadialGradientImpl) fn).getSize()[0]);
                      assertEquals("Correct size 2", tf.createPercent(12.0f), ((RadialGradientImpl) fn).getSize()[1]);
                      assertEquals("Shape is ellipse", tf.createIdent("ellipse"), ((RadialGradientImpl) fn).getShape());
                      assertEquals("Position is set", 2, ((RadialGradientImpl) fn).getPosition().length);
                      assertEquals("Position 1 is correct", tf.createPercent(50.0f), ((RadialGradientImpl) fn).getPosition()[0]);
                      assertEquals("Position 2 is correct", tf.createPercent(50.0f), ((RadialGradientImpl) fn).getPosition()[1]);
                      assertEquals("Two stops are set", 2, ((RadialGradientImpl) fn).getColorStops().size());
                      break;
                  case 17:
                      assertEquals(RadialGradientImpl.class, fn.getClass());
                      TermFunction fn2 = (TermFunction) d.get(1);
                      assertEquals(RadialGradientImpl.class, fn2.getClass());
                      break;
              }
          }
      }
      
      @Test
      public void gradientInvalid() throws IOException, CSSException
      {
          for (int i = 0; i < TEST_GRADIENT_INVALID.length; i++)
          {
              StyleSheet ss = CSSFactory.parseString(TEST_GRADIENT_INVALID[i], null);
              assertEquals("One rule is parset [" + i + "]", 1, ss.size());
              assertEquals("One property is set [" + i + "]", 1, ss.get(0).size());
          }        
      }
      
      @Test
      public void filterValid() throws IOException, CSSException
      {
          for (int i = 0; i < TEST_FILTER.length; i++)
          {
              StyleSheet ss = CSSFactory.parseString(TEST_FILTER[i], null);
              //System.out.println(i + " ss: " + ss);
              assertEquals("One rule is parset [" + i + "]", 1, ss.size());
              assertEquals("One property is set [" + i + "]", 1, ss.get(0).size());
              Declaration d = (Declaration) ss.get(0).get(0);
              TermFunction fn = (TermFunction) d.get(0);
              //System.out.println(i + ": " + d);
              switch (i)
              {
                  case 0:
                      assertEquals(BlurImpl.class, fn.getClass());
                      assertEquals("Radius is correct", tf.createLength("1.2", Unit.em, 1), ((BlurImpl) fn).getRadius());
                      break;
                  case 1:
                      assertEquals(BrightnessImpl.class, fn.getClass());
                      assertEquals("Amount is correct", 1.75f, ((BrightnessImpl) fn).getAmount(), 0.001f);
                      break;
                  case 2:
                      assertEquals(ContrastImpl.class, fn.getClass());
                      assertEquals("Amount is correct", 2.0f, ((ContrastImpl) fn).getAmount(), 0.001f);
                      break;
                  case 3:
                      assertEquals(DropShadowImpl.class, fn.getClass());
                      assertEquals("Offset X is correct", tf.createLength("30", Unit.px, 1), ((DropShadowImpl) fn).getOffsetX());
                      assertEquals("Offset Y is correct", tf.createLength("10", Unit.px, 1), ((DropShadowImpl) fn).getOffsetY());
                      assertEquals("Blur radius is correct", tf.createLength("4", Unit.px, 1), ((DropShadowImpl) fn).getBlurRadius());
                      assertEquals("Color is correct", tf.createColor("#4444dd"), ((DropShadowImpl) fn).getColor());
                      break;
                  case 4:
                      assertEquals(DropShadowImpl.class, fn.getClass());
                      assertEquals("Offset X is correct", tf.createLength("30", Unit.px, -1), ((DropShadowImpl) fn).getOffsetX());
                      assertEquals("Offset Y is correct", tf.createLength("10", Unit.px, -1), ((DropShadowImpl) fn).getOffsetY());
                      assertNull("Blur radius is enpty", ((DropShadowImpl) fn).getBlurRadius());
                      assertEquals("Color is correct", tf.createColor("#ff0000"), ((DropShadowImpl) fn).getColor());
                      break;
                  case 5:
                      assertEquals(GrayscaleImpl.class, fn.getClass());
                      assertEquals("Amount is correct", 1.0f, ((GrayscaleImpl) fn).getAmount(), 0.001f);
                      break;
                  case 6:
                      assertEquals(HueRotateImpl.class, fn.getClass());
                      assertEquals("Angle is correct", tf.createAngle("3.142", Unit.rad, -1), ((HueRotateImpl) fn).getAngle());
                      break;
                  case 7:
                      assertEquals(InvertImpl.class, fn.getClass());
                      assertEquals("Amount is correct", 0.3f, ((InvertImpl) fn).getAmount(), 0.001f);
                      break;
                  case 8:
                      assertEquals(OpacityImpl.class, fn.getClass());
                      assertEquals("Amount is correct", 0.0f, ((OpacityImpl) fn).getAmount(), 0.001f);
                      break;
                  case 9:
                      assertEquals(SaturateImpl.class, fn.getClass());
                      assertEquals("Amount is correct", 0.5f, ((SaturateImpl) fn).getAmount(), 0.001f);
                      break;
                  case 10:
                      assertEquals(SepiaImpl.class, fn.getClass());
                      assertEquals("Amount is correct", 1.0f, ((SepiaImpl) fn).getAmount(), 0.001f);
                      break;
              }
          }
      }
      
      @Test
      public void contentValid() throws IOException, CSSException
      {
          for (int i = 0; i < TEST_CONTENT.length; i++)
          {
              StyleSheet ss = CSSFactory.parseString(TEST_CONTENT[i], null);
              //System.out.println(i + " ss: " + ss);
              assertEquals("One rule is parset [" + i + "]", 1, ss.size());
              assertEquals("One property is set [" + i + "]", 1, ss.get(0).size());
              Declaration d = (Declaration) ss.get(0).get(0);
              TermFunction fn = (TermFunction) d.get(0);
              //System.out.println(i + ": " + d);
              switch (i)
              {
                  case 0:
                      assertEquals(AttrImpl.class, fn.getClass());
                      assertEquals("Name is correct", "title", ((AttrImpl) fn).getName());
                      break;
                  case 1:
                      assertEquals(CounterImpl.class, fn.getClass());
                      assertEquals("Name is correct", "chapter_counter", ((CounterImpl) fn).getName());
                      assertNull("Style is not set", ((CounterImpl) fn).getStyle());
                      break;
                  case 2:
                      assertEquals(CounterImpl.class, fn.getClass());
                      assertEquals("Name is correct", "chapter_counter", ((CounterImpl) fn).getName());
                      assertEquals("Style is correct", CSSProperty.ListStyleType.LOWER_ALPHA, ((CounterImpl) fn).getStyle()); 
                      break;
                  case 3:
                      assertEquals(CountersImpl.class, fn.getClass());
                      assertEquals("Name is correct", "chapter_counter", ((CountersImpl) fn).getName());
                      assertEquals("Separator is correct", "..", ((CountersImpl) fn).getSeparator());
                      assertNull("Style is not set", ((CountersImpl) fn).getStyle());
                      break;
                  case 4:
                      assertEquals(CountersImpl.class, fn.getClass());
                      assertEquals("Name is correct", "chapter_counter", ((CountersImpl) fn).getName());
                      assertEquals("Separator is correct", "::", ((CountersImpl) fn).getSeparator());
                      assertEquals("Style is correct", CSSProperty.ListStyleType.LOWER_ROMAN, ((CountersImpl) fn).getStyle()); 
                      break;
                  case 5:
                      assertEquals(CounterImpl.class, fn.getClass());
                      assertEquals("Name is correct", "c", ((CounterImpl) fn).getName());
                      assertEquals("Style is correct", CSSProperty.ListStyleType.NONE, ((CounterImpl) fn).getStyle());
                      TermString str = (TermString) d.get(1);
                      assertEquals("String is parsed", "z", str.getValue());
                      break;
              }
          }
      }
      
      @Test
      public void contentInvalid() throws IOException, CSSException
      {
  