/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2014, Open Source Geospatial Foundation (OSGeo) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. */ package org.geotools.styling.css; import static org.hamcrest.CoreMatchers.instanceOf; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.io.IOException; import java.util.Collections; import java.util.List; import java.util.Map; import org.geotools.filter.text.cql2.CQLException; import org.geotools.filter.text.ecql.ECQL; import org.geotools.styling.css.selector.Accept; import org.geotools.styling.css.selector.And; import org.geotools.styling.css.selector.Data; import org.geotools.styling.css.selector.Id; import org.geotools.styling.css.selector.Or; import org.geotools.styling.css.selector.PseudoClass; import org.geotools.styling.css.selector.ScaleRange; import org.geotools.styling.css.selector.Selector; import org.geotools.styling.css.selector.TypeName; import org.junit.Test; import org.parboiled.errors.ParseError; import org.parboiled.parserunners.ReportingParseRunner; import org.parboiled.support.ParsingResult; public class ParserSyntheticTest extends CssBaseTest { @Test public void simpleLine() throws IOException { String css = "* { stroke: #000000; }"; ParsingResult<Stylesheet> result = new ReportingParseRunner<Stylesheet>(parser.StyleSheet()) .run(css); assertNoErrors(result); Stylesheet ss = result.parseTreeRoot.getValue(); assertEquals(1, ss.getRules().size()); CssRule r = ss.getRules().get(0); assertTrue(r.getSelector() instanceof Accept); assertNull(r.getComment()); assertEquals(1, r.getProperties().size()); assertProperty(r, 0, "stroke", new Value.Literal("#000000")); } @Test public void simpleLineNamedColor() throws IOException { String css = "* { stroke: black; }"; ParsingResult<Stylesheet> result = new ReportingParseRunner<Stylesheet>(parser.StyleSheet()) .run(css); assertNoErrors(result); Stylesheet ss = result.parseTreeRoot.getValue(); assertEquals(1, ss.getRules().size()); CssRule r = ss.getRules().get(0); assertTrue(r.getSelector() instanceof Accept); assertNull(r.getComment()); assertEquals(1, r.getProperties().size()); assertProperty(r, 0, "stroke", new Value.Literal("#000000")); } @Test public void simpleLineNamedColorCaseInsensitive() throws IOException { String css = "* { stroke: BlAcK; }"; ParsingResult<Stylesheet> result = new ReportingParseRunner<Stylesheet>(parser.StyleSheet()) .run(css); assertNoErrors(result); Stylesheet ss = result.parseTreeRoot.getValue(); assertEquals(1, ss.getRules().size()); CssRule r = ss.getRules().get(0); assertTrue(r.getSelector() instanceof Accept); assertNull(r.getComment()); assertEquals(1, r.getProperties().size()); assertProperty(r, 0, "stroke", new Value.Literal("#000000")); } @Test public void lineWidth() throws IOException { String css = "* { stroke: #000000; stroke-width: 2;}"; ParsingResult<Stylesheet> result = new ReportingParseRunner<Stylesheet>(parser.StyleSheet()) .run(css); assertNoErrors(result); Stylesheet ss = result.parseTreeRoot.getValue(); assertEquals(1, ss.getRules().size()); CssRule r = ss.getRules().get(0); assertNull(r.getComment()); assertTrue(r.getSelector() instanceof Accept); assertEquals(2, r.getProperties().get(PseudoClass.ROOT).size()); assertProperty(r, 0, "stroke", new Value.Literal("#000000")); assertProperty(r, 1, "stroke-width", new Value.Literal("2")); // printResults(css, result); } @Test public void lineWidthPx() throws IOException { String css = "* { stroke: #000000; stroke-width: 2px;}"; ParsingResult<Stylesheet> result = new ReportingParseRunner<Stylesheet>(parser.StyleSheet()) .run(css); assertNoErrors(result); Stylesheet ss = result.parseTreeRoot.getValue(); assertEquals(1, ss.getRules().size()); CssRule r = ss.getRules().get(0); assertNull(r.getComment()); assertTrue(r.getSelector() instanceof Accept); assertEquals(2, r.getProperties().get(PseudoClass.ROOT).size()); assertProperty(r, 0, "stroke", new Value.Literal("#000000")); assertProperty(r, 1, "stroke-width", new Value.Literal("2px")); // printResults(css, result); } @Test public void lineColorAttribute() throws IOException, CQLException { assertLineColorExpression("* { stroke : [myColorAttribute]; }", "myColorAttribute"); } @Test public void lineColorDoubleQuotedAttribute() throws IOException, CQLException { assertLineColorExpression("* { stroke : [strSubstring(\"quotedAttribute\", 1, 5)]; }", "strSubstring(\"quotedAttribute\", 1, 5)"); } @Test public void lineColorStringInCQL() throws IOException, CQLException { assertLineColorExpression("* { stroke : [strSubstring('AB#00ffaaCDE', 2, 8)]; }", "strSubstring('AB#00ffaaCDE', 2, 8)"); } private void assertLineColorExpression(String css, String expectedExpression) throws CQLException { ParsingResult<Stylesheet> result = new ReportingParseRunner<Stylesheet>(parser.StyleSheet()) .run(css); assertNoErrors(result); // printResults(css, result); Stylesheet ss = result.parseTreeRoot.getValue(); assertEquals(1, ss.getRules().size()); CssRule r = ss.getRules().get(0); assertNull(r.getComment()); assertTrue(r.getSelector() instanceof Accept); assertEquals(1, r.getProperties().size()); assertProperty(r, 0, "stroke", new Value.Expression(ECQL.toExpression(expectedExpression))); } @Test public void staticStringDoubleQuote() throws IOException { String css = "* { label : \"test\"; }"; ParsingResult<Stylesheet> result = new ReportingParseRunner<Stylesheet>(parser.StyleSheet()) .run(css); assertNoErrors(result); Stylesheet ss = result.parseTreeRoot.getValue(); assertEquals(1, ss.getRules().size()); CssRule r = ss.getRules().get(0); assertNull(r.getComment()); assertTrue(r.getSelector() instanceof Accept); assertEquals(1, r.getProperties().size()); assertProperty(r, 0, "label", new Value.Literal("test")); // printResults(css, result); } @Test public void staticLabelSingleQuote() throws IOException { String css = "* { label : 'test'; }"; ParsingResult<Stylesheet> result = new ReportingParseRunner<Stylesheet>(parser.StyleSheet()) .run(css); assertNoErrors(result); Stylesheet ss = result.parseTreeRoot.getValue(); assertEquals(1, ss.getRules().size()); CssRule r = ss.getRules().get(0); assertNull(r.getComment()); assertTrue(r.getSelector() instanceof Accept); assertEquals(1, r.getProperties().size()); assertProperty(r, 0, "label", new Value.Literal("test")); // printResults(css, result); } @Test public void staticLabelFontFamily() throws IOException { String css = "* { label : 'test'; font-family: Arial; }"; ParsingResult<Stylesheet> result = new ReportingParseRunner<Stylesheet>(parser.StyleSheet()) .run(css); assertNoErrors(result); Stylesheet ss = result.parseTreeRoot.getValue(); assertEquals(1, ss.getRules().size()); CssRule r = ss.getRules().get(0); assertNull(r.getComment()); assertTrue(r.getSelector() instanceof Accept); assertEquals(2, r.getProperties().get(PseudoClass.ROOT).size()); assertProperty(r, 0, "label", new Value.Literal("test")); assertProperty(r, 1, "font-family", new Value.Literal("Arial")); // printResults(css, result); } @Test public void testFunction() throws IOException { String css = "* { mark: symbol(circle); }"; ParsingResult<Stylesheet> result = new ReportingParseRunner<Stylesheet>(parser.StyleSheet()) .run(css); assertNoErrors(result); Stylesheet ss = result.parseTreeRoot.getValue(); assertEquals(1, ss.getRules().size()); CssRule r = ss.getRules().get(0); assertNull(r.getComment()); assertTrue(r.getSelector() instanceof Accept); assertEquals(1, r.getProperties().size()); assertProperty( r, 0, "mark", new Value.Function("symbol", Collections.singletonList((Value) new Value.Literal( "circle")))); } @Test public void testMultiValue() throws IOException { String css = "* { label-anchor: 50% 50%; }"; ParsingResult<Stylesheet> result = new ReportingParseRunner<Stylesheet>(parser.StyleSheet()) .run(css); assertNoErrors(result); Stylesheet ss = result.parseTreeRoot.getValue(); assertEquals(1, ss.getRules().size()); CssRule r = ss.getRules().get(0); assertNull(r.getComment()); assertTrue(r.getSelector() instanceof Accept); assertEquals(1, r.getProperties().size()); assertProperty(r, 0, "label-anchor", new Value.MultiValue(new Value.Literal("50%"), new Value.Literal("50%"))); } @Test public void ogcSelector() throws IOException, CQLException { String css = "[myAttribute > 10] { stroke: #000000; }"; ParsingResult<Stylesheet> result = new ReportingParseRunner<Stylesheet>(parser.StyleSheet()) .run(css); assertNoErrors(result); Stylesheet ss = result.parseTreeRoot.getValue(); assertEquals(1, ss.getRules().size()); CssRule r = ss.getRules().get(0); assertNull(r.getComment()); assertTrue(r.getSelector() instanceof Data); Data of = (Data) r.getSelector(); assertEquals(ECQL.toFilter("myAttribute > 10"), of.filter); } @Test public void minScaleSelector() throws IOException, CQLException { String css = "[@scale > 1000] { stroke: #000000; }"; ParsingResult<Stylesheet> result = new ReportingParseRunner<Stylesheet>(parser.StyleSheet()) .run(css); assertNoErrors(result); Stylesheet ss = result.parseTreeRoot.getValue(); assertEquals(1, ss.getRules().size()); CssRule r = ss.getRules().get(0); assertNull(r.getComment()); assertTrue(r.getSelector() instanceof ScaleRange); ScaleRange sr = (ScaleRange) r.getSelector(); assertEquals(1000, sr.range.getMinValue(), 0d); assertEquals(Double.POSITIVE_INFINITY, sr.range.getMaxValue(), 0d); } @Test public void maxScaleSelector() throws IOException, CQLException { String css = "[@scale < 1000] { stroke: #000000; }"; ParsingResult<Stylesheet> result = new ReportingParseRunner<Stylesheet>(parser.StyleSheet()) .run(css); assertNoErrors(result); Stylesheet ss = result.parseTreeRoot.getValue(); assertEquals(1, ss.getRules().size()); CssRule r = ss.getRules().get(0); assertNull(r.getComment()); assertTrue(r.getSelector() instanceof ScaleRange); ScaleRange sr = (ScaleRange) r.getSelector(); assertEquals(0, sr.range.getMinValue(), 0d); assertEquals(1000, sr.range.getMaxValue(), 0d); } @Test public void qualifiedTypeNameSelector() throws IOException, CQLException { String css = "topp:states { stroke: #000000; }"; ParsingResult<Stylesheet> result = new ReportingParseRunner<Stylesheet>(parser.StyleSheet()) .run(css); assertNoErrors(result); Stylesheet ss = result.parseTreeRoot.getValue(); assertEquals(1, ss.getRules().size()); CssRule r = ss.getRules().get(0); assertNull(r.getComment()); assertTrue(r.getSelector() instanceof TypeName); TypeName s = (TypeName) r.getSelector(); assertEquals("topp:states", s.name); } @Test public void simpleTypeNameSelector() throws IOException, CQLException { String css = "states { stroke: #000000; }"; ParsingResult<Stylesheet> result = new ReportingParseRunner<Stylesheet>(parser.StyleSheet()) .run(css); assertNoErrors(result); Stylesheet ss = result.parseTreeRoot.getValue(); assertEquals(1, ss.getRules().size()); CssRule r = ss.getRules().get(0); assertNull(r.getComment()); assertTrue(r.getSelector() instanceof TypeName); TypeName s = (TypeName) r.getSelector(); assertEquals("states", s.name); } @Test public void idSelector() throws IOException, CQLException { String css = "#states.2 { stroke: #000000; }"; ParsingResult<Stylesheet> result = new ReportingParseRunner<Stylesheet>(parser.StyleSheet()) .run(css); assertNoErrors(result); Stylesheet ss = result.parseTreeRoot.getValue(); assertEquals(1, ss.getRules().size()); CssRule r = ss.getRules().get(0); assertNull(r.getComment()); assertTrue(r.getSelector() instanceof Id); Id s = (Id) r.getSelector(); assertEquals(1, s.ids.size()); assertEquals("states.2", s.ids.iterator().next()); } @Test public void pseudoClassSelector() throws IOException, CQLException { String css = ":mark { stroke: #000000; }"; ParsingResult<Stylesheet> result = new ReportingParseRunner<Stylesheet>(parser.StyleSheet()) .run(css); assertNoErrors(result); Stylesheet ss = result.parseTreeRoot.getValue(); assertEquals(1, ss.getRules().size()); CssRule r = ss.getRules().get(0); assertNull(r.getComment()); assertTrue(r.getSelector() instanceof PseudoClass); PseudoClass s = (PseudoClass) r.getSelector(); assertEquals("mark", s.getClassName()); assertEquals(-1, s.getNumber()); } @Test public void numberedPseudoClassSelector() throws IOException, CQLException { String css = ":nth-mark(2) { stroke: #000000; }"; ParsingResult<Stylesheet> result = new ReportingParseRunner<Stylesheet>(parser.StyleSheet()) .run(css); assertNoErrors(result); Stylesheet ss = result.parseTreeRoot.getValue(); assertEquals(1, ss.getRules().size()); CssRule r = ss.getRules().get(0); assertNull(r.getComment()); assertTrue(r.getSelector() instanceof PseudoClass); PseudoClass s = (PseudoClass) r.getSelector(); assertEquals("mark", s.getClassName()); assertEquals(2, s.getNumber()); } @Test public void andDataScale() throws IOException, CQLException { String css = "[att < 15] [@scale > 3000] { stroke: #000000; }"; ParsingResult<Stylesheet> result = new ReportingParseRunner<Stylesheet>(parser.StyleSheet()) .run(css); assertNoErrors(result); Stylesheet ss = result.parseTreeRoot.getValue(); assertEquals(1, ss.getRules().size()); CssRule r = ss.getRules().get(0); assertNull(r.getComment()); assertTrue(r.getSelector() instanceof And); And s = (And) r.getSelector(); assertEquals(new Data(ECQL.toFilter("att < 15")), s.getChildren().get(0)); assertEquals(new ScaleRange(3000, true, Double.POSITIVE_INFINITY, true), s.getChildren() .get(1)); } @Test public void andTypeScale() throws IOException, CQLException { String css = "topp:states [@scale > 3000] { stroke: #000000; }"; ParsingResult<Stylesheet> result = new ReportingParseRunner<Stylesheet>(parser.StyleSheet()) .run(css); assertNoErrors(result); Stylesheet ss = result.parseTreeRoot.getValue(); assertEquals(1, ss.getRules().size()); CssRule r = ss.getRules().get(0); assertNull(r.getComment()); assertTrue(r.getSelector() instanceof And); And s = (And) r.getSelector(); assertEquals(new TypeName("topp:states"), s.getChildren().get(0)); assertEquals(new ScaleRange(3000, true, Double.POSITIVE_INFINITY, true), s.getChildren() .get(1)); } @Test public void orDataScale() throws IOException, CQLException { String css = "[att < 15], [@scale > 3000] { stroke: #000000; }"; ParsingResult<Stylesheet> result = new ReportingParseRunner<Stylesheet>(parser.StyleSheet()) .run(css); assertNoErrors(result); Stylesheet ss = result.parseTreeRoot.getValue(); assertEquals(1, ss.getRules().size()); CssRule r = ss.getRules().get(0); assertNull(r.getComment()); assertTrue(r.getSelector() instanceof Or); Or s = (Or) r.getSelector(); assertEquals(new Data(ECQL.toFilter("att < 15")), s.getChildren().get(0)); assertEquals(new ScaleRange(3000, true, Double.POSITIVE_INFINITY, true), s.getChildren() .get(1)); } @Test public void orTypeScale() throws IOException, CQLException { String css = "topp:states, [@scale > 3000] { stroke: #000000; }"; ParsingResult<Stylesheet> result = new ReportingParseRunner<Stylesheet>(parser.StyleSheet()) .run(css); assertNoErrors(result); Stylesheet ss = result.parseTreeRoot.getValue(); assertEquals(1, ss.getRules().size()); CssRule r = ss.getRules().get(0); assertNull(r.getComment()); assertTrue(r.getSelector() instanceof Or); Or s = (Or) r.getSelector(); assertEquals(new TypeName("topp:states"), s.getChildren().get(0)); assertEquals(new ScaleRange(3000, true, Double.POSITIVE_INFINITY, true), s.getChildren() .get(1)); } @Test public void orIdentifiers() throws IOException, CQLException { String css = "#states.2, #states.3 { stroke: #000000; }"; ParsingResult<Stylesheet> result = new ReportingParseRunner<Stylesheet>(parser.StyleSheet()) .run(css); assertNoErrors(result); Stylesheet ss = result.parseTreeRoot.getValue(); assertEquals(1, ss.getRules().size()); CssRule r = ss.getRules().get(0); assertNull(r.getComment()); assertTrue(r.getSelector() instanceof Or); Or s = (Or) r.getSelector(); assertEquals(new Id("states.2"), s.getChildren().get(0)); assertEquals(new Id("states.3"), s.getChildren().get(1)); } @Test public void nestedOr() throws IOException, CQLException { String css = "#states.2, #states.3, [myAtt > 10] { stroke: #000000; }"; ParsingResult<Stylesheet> result = new ReportingParseRunner<Stylesheet>(parser.StyleSheet()) .run(css); assertNoErrors(result); Stylesheet ss = result.parseTreeRoot.getValue(); assertEquals(1, ss.getRules().size()); CssRule r = ss.getRules().get(0); assertTrue(r.getSelector() instanceof Or); assertNull(r.getComment()); Or s = (Or) r.getSelector(); assertEquals(new Id("states.2"), s.getChildren().get(0)); assertEquals(new Id("states.3"), s.getChildren().get(1)); assertEquals(new Data(ECQL.toFilter("myAtt > 10")), s.getChildren().get(2)); } @Test public void nestedAnd() throws IOException, CQLException { String css = "#states.2 [@scale > 1000] [myAtt > 10] { stroke: #000000; }"; ParsingResult<Stylesheet> result = new ReportingParseRunner<Stylesheet>(parser.StyleSheet()) .run(css); assertNoErrors(result); Stylesheet ss = result.parseTreeRoot.getValue(); assertEquals(1, ss.getRules().size()); CssRule r = ss.getRules().get(0); assertTrue(r.getSelector() instanceof And); assertNull(r.getComment()); And s = (And) r.getSelector(); assertEquals(new Id("states.2"), s.getChildren().get(0)); assertEquals(new ScaleRange(1000, true, Double.POSITIVE_INFINITY, true), s.getChildren() .get(1)); assertEquals(new Data(ECQL.toFilter("myAtt > 10")), s.getChildren().get(2)); } @Test public void nestedOrAnd() throws IOException, CQLException { String css = "#states.2 [@scale > 1000], [myAtt > 10] { stroke: #000000; }"; ParsingResult<Stylesheet> result = new ReportingParseRunner<Stylesheet>(parser.StyleSheet()) .run(css); assertNoErrors(result); Stylesheet ss = result.parseTreeRoot.getValue(); assertEquals(1, ss.getRules().size()); CssRule r = ss.getRules().get(0); assertNull(r.getComment()); assertTrue(r.getSelector() instanceof Or); Or s = (Or) r.getSelector(); assertEquals(new Data(ECQL.toFilter("myAtt > 10")), s.getChildren().get(1)); assertTrue(s.getChildren().get(0) instanceof And); And and = (And) s.getChildren().get(0); assertEquals(new Id("states.2"), and.getChildren().get(0)); assertEquals(new ScaleRange(1000, true, Double.POSITIVE_INFINITY, true), and.getChildren() .get(1)); } @Test public void testComment() { String css = "/* This is a comment */ * { stroke: #000000; }"; ParsingResult<Stylesheet> result = new ReportingParseRunner<Stylesheet>(parser.StyleSheet()) .run(css); assertNoErrors(result); Stylesheet ss = result.parseTreeRoot.getValue(); assertEquals(1, ss.getRules().size()); CssRule r = ss.getRules().get(0); assertEquals("This is a comment", r.getComment()); assertTrue(r.getSelector() instanceof Accept); } @Test public void testCommentMultiline() { String commentInternal = "* @title The title\n" + // " * @abstract The abstract"; String comment = "/*\n" + // commentInternal + // "\n*/"; String css = comment + " * { stroke: #000000; }"; ParsingResult<Stylesheet> result = new ReportingParseRunner<Stylesheet>(parser.StyleSheet()) .run(css); assertNoErrors(result); Stylesheet ss = result.parseTreeRoot.getValue(); assertEquals(1, ss.getRules().size()); CssRule r = ss.getRules().get(0); assertEquals(commentInternal, r.getComment()); assertTrue(r.getSelector() instanceof Accept); } @Test public void testCommentAmongProperties1() { String css = "* { stroke: #000000; /* This is a comment */}"; ParsingResult<Stylesheet> result = new ReportingParseRunner<Stylesheet>(parser.StyleSheet()) .run(css); assertNoErrors(result); Stylesheet ss = result.parseTreeRoot.getValue(); assertEquals(1, ss.getRules().size()); CssRule r = ss.getRules().get(0); assertTrue(r.getSelector() instanceof Accept); assertProperty(r, 0, "stroke", new Value.Literal("#000000")); } @Test public void testCommentAmongProperties2() { String css = "* { /* This is a comment */ stroke: #000000;}"; ParsingResult<Stylesheet> result = new ReportingParseRunner<Stylesheet>(parser.StyleSheet()) .run(css); assertNoErrors(result); Stylesheet ss = result.parseTreeRoot.getValue(); assertEquals(1, ss.getRules().size()); CssRule r = ss.getRules().get(0); assertTrue(r.getSelector() instanceof Accept); } @Test public void testMultiRule() { String css = "* {stroke: #000000;} states {fill:red;} "; ParsingResult<Stylesheet> result = new ReportingParseRunner<Stylesheet>(parser.StyleSheet()) .run(css); assertNoErrors(result); Stylesheet ss = result.parseTreeRoot.getValue(); assertEquals(2, ss.getRules().size()); CssRule r = ss.getRules().get(0); assertTrue(r.getSelector() instanceof Accept); assertProperty(r, 0, "stroke", new Value.Literal("#000000")); r = ss.getRules().get(1); assertEquals(r.getSelector(), new TypeName("states")); assertProperty(r, 0, "fill", new Value.Literal("#ff0000")); } @Test public void testExceptionMessage() { // lets forget a semicolon in the second line String css = "* \n { fill: blue \n stroke: yellow }"; try { CssParser.parse(css); fail("Should have failed with an exception"); } catch (CSSParseException e) { List<ParseError> errors = e.getErrors(); assertEquals(1, errors.size()); } } @Test public void testMissingLastSemicolon() { // lets forget a semicolon in the last line, that's actually fine, the ; is a separator, not // a terminator String css = "* \n { fill: blue; \n stroke: yellow }"; Stylesheet ss = CssParser.parse(css); assertEquals(1, ss.getRules().size()); Map<PseudoClass, List<Property>> properties = ss.getRules().get(0).getProperties(); assertEquals(2, properties.get(PseudoClass.ROOT).size()); } @Test public void testDirectives() { String css = "@first \"1\"; \n @second \"2\"; \n * { fill: blue;}"; Stylesheet ss = CssParser.parse(css); List<Directive> directives = ss.getDirectives(); assertEquals(2, directives.size()); assertEquals("first", directives.get(0).getName()); assertEquals("1", directives.get(0).getValue()); assertEquals("second", directives.get(1).getName()); assertEquals("2", directives.get(1).getValue()); assertEquals(1, ss.getRules().size()); CssRule r = ss.getRules().get(0); assertTrue(r.getSelector() instanceof Accept); assertProperty(r, 0, "fill", new Value.Literal("#0000ff")); } @Test public void testSameLinePrefixComment() { String css = "* { /* prefix comment */ fill: blue }"; Stylesheet ss = CssParser.parse(css); assertEquals(1, ss.getRules().size()); CssRule r = ss.getRules().get(0); assertProperty(r, 0, "fill", new Value.Literal("#0000ff")); } @Test public void testSameLineSuffixComment() { String css = "* { fill: blue; /* prefix comment */ }"; Stylesheet ss = CssParser.parse(css); assertEquals(1, ss.getRules().size()); CssRule r = ss.getRules().get(0); assertProperty(r, 0, "fill", new Value.Literal("#0000ff")); } @Test public void testSameCommentBetweenRules() { String css = "* { fill: blue; \n /* prefix comment */ \n stroke: black }"; Stylesheet ss = CssParser.parse(css); assertEquals(1, ss.getRules().size()); CssRule r = ss.getRules().get(0); assertProperty(r, 0, "fill", new Value.Literal("#0000ff")); assertProperty(r, 1, "stroke", new Value.Literal("#000000")); } @Test public void testEmptyComment() { String css = "* { fill: blue; /**/ stroke: yellow}"; Stylesheet ss = CssParser.parse(css); assertEquals(1, ss.getRules().size()); } @Test public void testSimpleTransform() { String css = "* { transform: ras:Contour(levels: 1100 1200 1300); stroke: black}"; Stylesheet ss = CssParser.parse(css); assertEquals(1, ss.getRules().size()); CssRule rule = ss.getRules().get(0); final Value.MultiValue levelsValue = new Value.MultiValue(new Value.Literal("1100"), new Value.Literal("1200"), new Value.Literal("1300")); assertProperty(rule, 0, "transform", new Value.TransformFunction("ras:Contour", Collections.singletonMap("levels", levelsValue))); assertProperty(rule, 1, "stroke", new Value.Literal("#000000")); } @Test public void testMarkSubrule() { String css = "* { mark: symbol(circle); :mark { size: 10}}"; Stylesheet ss = CssParser.parse(css); assertEquals(1, ss.getRules().size()); CssRule rule = ss.getRules().get(0); assertEquals(1, rule.getNestedRules().size()); CssRule nested = rule.getNestedRules().get(0); PseudoClass selector = assertSelector(nested.getSelector(), PseudoClass.class); assertEquals("mark", selector.getClassName()); assertProperty(nested, PseudoClass.newPseudoClass("mark"), 0, "size", new Value.Literal("10")); } @Test public void testSubruleWithNoTitle() { String css = "/* @title All */ * { mark: symbol(circle); :mark { size: 10}}"; Stylesheet ss = CssParser.parse(css); assertEquals(1, ss.getRules().size()); CssRule rule = ss.getRules().get(0); assertEquals("@title All", rule.getComment()); assertEquals(1, rule.getNestedRules().size()); CssRule nested = rule.getNestedRules().get(0); assertNull(nested.getComment()); } @Test public void testSubruleWithTitle() { String css = "/* @title All */ * { mark: symbol(circle); /* @title special */ [a = 10] { mark: symbol(triangle)}}"; Stylesheet ss = CssParser.parse(css); assertEquals(1, ss.getRules().size()); CssRule rule = ss.getRules().get(0); assertEquals("@title All", rule.getComment()); assertEquals(1, rule.getNestedRules().size()); CssRule nested = rule.getNestedRules().get(0); assertEquals("@title special", nested.getComment()); } @Test public void testFeatureTypeSubrule() { String css = "* { fill: red; topp:states { stroke: yellow }}"; Stylesheet ss = CssParser.parse(css); assertEquals(1, ss.getRules().size()); CssRule rule = ss.getRules().get(0); assertEquals(1, rule.getNestedRules().size()); CssRule nested = rule.getNestedRules().get(0); TypeName selector = assertSelector(nested.getSelector(), TypeName.class); assertEquals("topp:states", selector.name); assertProperty(nested, 0, "stroke", new Value.Literal("#ffff00")); } @Test public void testMultiNestedSelectors() { String css = "* { stroke: black; [a <= 10] { fill: yellow }; [a > 10] { fill: red}; stroke-opacity: 50%}"; Stylesheet ss = CssParser.parse(css); assertEquals(1, ss.getRules().size()); CssRule rule = ss.getRules().get(0); assertEquals(2, rule.getNestedRules().size()); } private <T extends Selector> T assertSelector(Selector selector, Class<T> clazz) { assertThat(selector, instanceOf(clazz)); return (T) selector; } }