/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2014 - 2016, 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.*;
import java.awt.Color;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import javax.xml.transform.TransformerException;
import org.geotools.filter.function.color.DarkenFunction;
import org.geotools.filter.text.cql2.CQLException;
import org.geotools.filter.text.ecql.ECQL;
import org.geotools.styling.AnchorPoint;
import org.geotools.styling.ColorMap;
import org.geotools.styling.ExternalGraphic;
import org.geotools.styling.Fill;
import org.geotools.styling.Font;
import org.geotools.styling.Graphic;
import org.geotools.styling.Halo;
import org.geotools.styling.LinePlacement;
import org.geotools.styling.LineSymbolizer;
import org.geotools.styling.PointPlacement;
import org.geotools.styling.PointSymbolizer;
import org.geotools.styling.PolygonSymbolizer;
import org.geotools.styling.RasterSymbolizer;
import org.geotools.styling.SLDTransformer;
import org.geotools.styling.SelectedChannelType;
import org.geotools.styling.Stroke;
import org.geotools.styling.TextSymbolizer;
import org.geotools.styling.TextSymbolizer2;
import org.junit.Test;
import org.opengis.filter.Filter;
import org.opengis.filter.expression.Expression;
import org.opengis.filter.expression.Function;
import org.opengis.filter.expression.Literal;
import org.opengis.style.ContrastMethod;
import org.opengis.style.Displacement;
import org.opengis.style.FeatureTypeStyle;
import org.opengis.style.GraphicFill;
import org.opengis.style.Mark;
import org.opengis.style.Rule;
import org.opengis.style.Style;
import org.opengis.style.Symbolizer;
public class TranslatorSyntheticTest extends CssBaseTest {
private void assertLiteral(String value, Expression ex) {
assertTrue(ex instanceof Literal);
String actual = ex.evaluate(null, String.class);
assertEquals(value, actual);
}
private void assertExpression(String expectedCql, Expression actual) throws CQLException {
Expression expected = ECQL.toExpression(expectedCql);
assertEquals(expected, actual);
}
private void assertVendorOption(String expectedValue, String name,
org.geotools.styling.Symbolizer ps) {
assertEquals(expectedValue, ps.getOptions().get(name));
}
private Rule assertSingleRule(Style style) {
FeatureTypeStyle fts = assertSingleFeatureTypeStyle(style);
assertEquals("Expected 1 rule, found " + fts.rules().size(), 1, fts.rules().size());
return fts.rules().get(0);
}
private FeatureTypeStyle assertSingleFeatureTypeStyle(Style style) {
assertEquals(
"Expected single feature type style, found " + style.featureTypeStyles().size(), 1,
style.featureTypeStyles().size());
FeatureTypeStyle fts = style.featureTypeStyles().get(0);
return fts;
}
private <T extends Symbolizer> T assertSingleSymbolizer(Rule rule, Class<T> symbolizerType) {
assertEquals(1, rule.symbolizers().size());
assertTrue(symbolizerType.isInstance(rule.symbolizers().get(0)));
return (T) rule.symbolizers().get(0);
}
@Test
public void fillBasic() throws IOException {
String css = "* { fill: orange; }";
Style style = translate(css);
Rule rule = assertSingleRule(style);
PolygonSymbolizer ps = assertSingleSymbolizer(rule, PolygonSymbolizer.class);
Fill fill = ps.getFill();
assertLiteral("#ffa500", fill.getColor());
assertLiteral("1", fill.getOpacity());
assertNull(fill.getGraphicFill());
}
@Test
public void fillOpacity() throws Exception {
String css = "* { fill: orange; fill-opacity: 0.5; }";
Style style = translate(css);
Rule rule = assertSingleRule(style);
PolygonSymbolizer ps = assertSingleSymbolizer(rule, PolygonSymbolizer.class);
Fill fill = ps.getFill();
assertLiteral("#ffa500", fill.getColor());
assertLiteral("0.5", fill.getOpacity());
assertNull(fill.getGraphicFill());
}
@Test
public void fillCircleDefaults() throws Exception {
String css = "* { fill: symbol('circle'); }";
Style style = translate(css);
Rule rule = assertSingleRule(style);
PolygonSymbolizer ps = assertSingleSymbolizer(rule, PolygonSymbolizer.class);
Fill fill = ps.getFill();
GraphicFill gf = fill.getGraphicFill();
assertEquals(1, gf.graphicalSymbols().size());
Mark mark = (Mark) gf.graphicalSymbols().get(0);
assertLiteral("circle", mark.getWellKnownName());
Fill markFill = (Fill) mark.getFill();
assertLiteral("#808080", markFill.getColor());
Stroke markStroke = (Stroke) mark.getStroke();
assertLiteral("#000000", markStroke.getColor());
assertLiteral("1", markStroke.getWidth());
assertNull(gf.getSize());
assertLiteral("1.0", gf.getOpacity());
assertLiteral("0", gf.getRotation());
}
@Test
public void fillCircleFilledMark() throws Exception {
String css = "* { fill: symbol('circle');} :fill { fill: yellow;}";
Style style = translate(css);
Rule rule = assertSingleRule(style);
PolygonSymbolizer ps = assertSingleSymbolizer(rule, PolygonSymbolizer.class);
Fill fill = ps.getFill();
Graphic gf = fill.getGraphicFill();
assertEquals(1, gf.graphicalSymbols().size());
Mark mark = (Mark) gf.graphicalSymbols().get(0);
assertLiteral("circle", mark.getWellKnownName());
assertLiteral("#ffff00", mark.getFill().getColor());
assertNull(mark.getStroke());
assertNull(gf.getSize());
assertLiteral("1.0", gf.getOpacity());
assertLiteral("0", gf.getRotation());
}
@Test
public void fillCircleStrokedMark() throws Exception {
String css = "* { fill: symbol('circle');} :fill { stroke: black;}";
Style style = translate(css);
Rule rule = assertSingleRule(style);
PolygonSymbolizer ps = assertSingleSymbolizer(rule, PolygonSymbolizer.class);
Fill fill = ps.getFill();
Graphic gf = fill.getGraphicFill();
assertEquals(1, gf.graphicalSymbols().size());
Mark mark = (Mark) gf.graphicalSymbols().get(0);
assertLiteral("circle", mark.getWellKnownName());
assertNull(mark.getFill());
assertLiteral("#000000", mark.getStroke().getColor());
assertNull(gf.getSize());
assertLiteral("1.0", gf.getOpacity());
assertLiteral("0", gf.getRotation());
}
@Test
public void fillCircleFilledStrokedMark() throws Exception {
String css = "* { fill: symbol('circle');} :fill { fill: yellow; stroke: black; stroke-width: 3;}";
Style style = translate(css);
Rule rule = assertSingleRule(style);
PolygonSymbolizer ps = assertSingleSymbolizer(rule, PolygonSymbolizer.class);
Fill fill = ps.getFill();
Graphic gf = fill.getGraphicFill();
assertEquals(1, gf.graphicalSymbols().size());
Mark mark = (Mark) gf.graphicalSymbols().get(0);
assertLiteral("circle", mark.getWellKnownName());
assertLiteral("#ffff00", mark.getFill().getColor());
assertLiteral("#000000", mark.getStroke().getColor());
assertLiteral("3", mark.getStroke().getWidth());
assertNull(gf.getSize());
assertLiteral("1.0", gf.getOpacity());
assertLiteral("0", gf.getRotation());
}
@Test
public void fillExternalGraphic() throws Exception {
String css = "* { fill: url(test.svg); fill-rotation: 45; fill-size: 35;}";
Style style = translate(css);
Rule rule = assertSingleRule(style);
PolygonSymbolizer ps = assertSingleSymbolizer(rule, PolygonSymbolizer.class);
Fill fill = ps.getFill();
GraphicFill gf = fill.getGraphicFill();
assertEquals(1, gf.graphicalSymbols().size());
ExternalGraphic eg = (ExternalGraphic) gf.graphicalSymbols().get(0);
assertEquals("test.svg", eg.getURI());
assertLiteral("35", gf.getSize());
assertLiteral("1.0", gf.getOpacity());
assertLiteral("45", gf.getRotation());
}
@Test
public void fillVendorOptions() throws Exception {
String css = "* { fill: yellow; -gt-fill-label-obstacle: true;} ";
Style style = translate(css);
Rule rule = assertSingleRule(style);
PolygonSymbolizer ps = assertSingleSymbolizer(rule, PolygonSymbolizer.class);
Fill fill = ps.getFill();
assertLiteral("#ffff00", fill.getColor());
assertVendorOption("true", "labelObstacle", ps);
}
@Test
public void fillGeometry() throws Exception {
String css = "* { fill-geometry: [centroid(theGeom)]; fill: red;} ";
Style style = translate(css);
Rule rule = assertSingleRule(style);
PolygonSymbolizer ps = assertSingleSymbolizer(rule, PolygonSymbolizer.class);
assertExpression("centroid(theGeom)", ps.getGeometry());
Fill fill = ps.getFill();
assertLiteral("#ff0000", fill.getColor());
}
@Test
public void strokeBasic() throws IOException {
String css = "* { stroke: orange; }";
Style style = translate(css);
Rule rule = assertSingleRule(style);
LineSymbolizer ls = assertSingleSymbolizer(rule, LineSymbolizer.class);
Stroke stroke = ls.getStroke();
assertLiteral("#ffa500", stroke.getColor());
assertLiteral("1", stroke.getWidth());
assertLiteral("1", stroke.getOpacity());
assertLiteral("butt", stroke.getLineCap());
assertLiteral("miter", stroke.getLineJoin());
assertNull(stroke.getDashArray());
assertLiteral("0", stroke.getDashOffset());
assertNull(stroke.getGraphicFill());
assertNull(stroke.getGraphicStroke());
}
@Test
public void strokeDashed() throws IOException {
String css = "* { stroke: orange; stroke-width: 10; stroke-dasharray: 10 5 1 5; stroke-dashoffset: 2; stroke-linecap: round; stroke-linejoin: round;}";
Style style = translate(css);
Rule rule = assertSingleRule(style);
LineSymbolizer ls = assertSingleSymbolizer(rule, LineSymbolizer.class);
Stroke stroke = ls.getStroke();
assertLiteral("#ffa500", stroke.getColor());
assertLiteral("10", stroke.getWidth());
assertLiteral("1", stroke.getOpacity());
assertLiteral("round", stroke.getLineCap());
assertLiteral("round", stroke.getLineJoin());
assertTrue(Arrays.equals(new float[] { 10, 5, 1, 5 }, stroke.getDashArray()));
assertLiteral("2", stroke.getDashOffset());
assertNull(stroke.getGraphicFill());
assertNull(stroke.getGraphicStroke());
}
@Test
public void strokeRepeatedMark() throws IOException {
String css = "* { stroke: symbol('square');} :stroke { fill: red; }";
Style style = translate(css);
Rule rule = assertSingleRule(style);
LineSymbolizer ls = assertSingleSymbolizer(rule, LineSymbolizer.class);
Stroke stroke = ls.getStroke();
Graphic graphic = stroke.getGraphicStroke();
assertEquals(1, graphic.graphicalSymbols().size());
Mark mark = (Mark) graphic.graphicalSymbols().get(0);
assertLiteral("square", mark.getWellKnownName());
assertLiteral("#ff0000", mark.getFill().getColor());
}
@Test
public void strokeFillMark() throws IOException {
String css = "* { stroke: symbol('square'); stroke-size: 20; stroke-repeat: stipple;} :stroke { fill: red; }";
Style style = translate(css);
Rule rule = assertSingleRule(style);
LineSymbolizer ls = assertSingleSymbolizer(rule, LineSymbolizer.class);
Stroke stroke = ls.getStroke();
Graphic graphic = stroke.getGraphicFill();
assertEquals(1, graphic.graphicalSymbols().size());
Mark mark = (Mark) graphic.graphicalSymbols().get(0);
assertLiteral("square", mark.getWellKnownName());
assertLiteral("#ff0000", mark.getFill().getColor());
assertLiteral("20", graphic.getSize());
}
@Test
public void fillStroke() throws Exception {
String css = "* { fill: red; stroke: yellow;} ";
Style style = translate(css);
Rule rule = assertSingleRule(style);
PolygonSymbolizer ps = assertSingleSymbolizer(rule, PolygonSymbolizer.class);
Fill fill = ps.getFill();
assertLiteral("#ff0000", fill.getColor());
Stroke stroke = ps.getStroke();
assertLiteral("#ffff00", stroke.getColor());
}
@Test
public void fillStrokeLabelObstacle() throws Exception {
String css = "* { fill: red; stroke: yellow; -gt-stroke-label-obstacle: true;} ";
Style style = translate(css);
Rule rule = assertSingleRule(style);
assertEquals(2, rule.symbolizers().size());
PolygonSymbolizer ps = (PolygonSymbolizer) rule.symbolizers().get(0);
Fill fill = ps.getFill();
assertLiteral("#ff0000", fill.getColor());
assertTrue(ps.getOptions().isEmpty());
LineSymbolizer ls = (LineSymbolizer) rule.symbolizers().get(1);
Stroke stroke = ls.getStroke();
assertLiteral("#ffff00", stroke.getColor());
assertVendorOption("true", "labelObstacle", ls);
}
@Test
public void mark() throws Exception {
String css = "* { mark: symbol(circle); mark-size: 10; mark-rotation: 45; mark-geometry: [centroid(the_geom)];} :mark { fill: blue; }";
Style style = translate(css);
Rule rule = assertSingleRule(style);
PointSymbolizer ps = assertSingleSymbolizer(rule, PointSymbolizer.class);
assertExpression("centroid(the_geom)", ps.getGeometry());
Graphic g = ps.getGraphic();
assertLiteral("10", g.getSize());
assertLiteral("45", g.getRotation());
assertNotNull(g);
assertEquals(1, g.graphicalSymbols().size());
Mark mark = (Mark) g.graphicalSymbols().get(0);
assertLiteral("circle", mark.getWellKnownName());
assertLiteral("#0000ff", mark.getFill().getColor());
}
@Test
public void markAnchorDisplacement() throws Exception {
String css = "* { mark: symbol(circle); mark-size: 10; mark-anchor: 0 1; mark-offset: 10 20;}";
Style style = translate(css);
Rule rule = assertSingleRule(style);
PointSymbolizer ps = assertSingleSymbolizer(rule, PointSymbolizer.class);
Graphic g = ps.getGraphic();
AnchorPoint ap = g.getAnchorPoint();
assertNotNull(ap);
assertEquals(0, ap.getAnchorPointX().evaluate(null, Double.class), 0d);
assertEquals(1, ap.getAnchorPointY().evaluate(null, Double.class), 0d);
Displacement d = g.getDisplacement();
assertNotNull(d);
assertEquals(10, d.getDisplacementX().evaluate(null, Double.class), 0d);
assertEquals(20, d.getDisplacementY().evaluate(null, Double.class), 0d);
}
@Test
public void markAnchorDisplacementSingleValue() throws Exception {
String css = "* { mark: symbol(circle); mark-size: 10; mark-anchor: 0.5; mark-offset: 10;}";
Style style = translate(css);
Rule rule = assertSingleRule(style);
PointSymbolizer ps = assertSingleSymbolizer(rule, PointSymbolizer.class);
Graphic g = ps.getGraphic();
AnchorPoint ap = g.getAnchorPoint();
assertNotNull(ap);
assertEquals(0.5, ap.getAnchorPointX().evaluate(null, Double.class), 0d);
assertEquals(0.5, ap.getAnchorPointY().evaluate(null, Double.class), 0d);
Displacement d = g.getDisplacement();
assertNotNull(d);
assertEquals(10, d.getDisplacementX().evaluate(null, Double.class), 0d);
assertEquals(10, d.getDisplacementY().evaluate(null, Double.class), 0d);
}
@Test
public void externalGraphic() throws Exception {
String css = "* { mark: url(test.svg); mark-size: 10; mark-rotation: 45; mark-mime: 'image/png';}";
Style style = translate(css);
Rule rule = assertSingleRule(style);
PointSymbolizer ps = assertSingleSymbolizer(rule, PointSymbolizer.class);
Graphic g = ps.getGraphic();
assertLiteral("10", g.getSize());
assertLiteral("45", g.getRotation());
assertNotNull(g);
assertEquals(1, g.graphicalSymbols().size());
ExternalGraphic eg = (ExternalGraphic) g.graphicalSymbols().get(0);
assertEquals("image/png", eg.getFormat());
assertEquals("test.svg", eg.getURI());
}
@Test
public void labelBasic() throws Exception {
String css = "* { label: 'test'; label-geometry: [centroid(the_geom)];}";
Style style = translate(css);
Rule rule = assertSingleRule(style);
TextSymbolizer ts = assertSingleSymbolizer(rule, TextSymbolizer.class);
assertLiteral("test", ts.getLabel());
assertExpression("centroid(the_geom)", ts.getGeometry());
}
@Test
public void labelPointPlacement() throws Exception {
String css = "* { label: 'test'; label-offset: 5 5; label-rotation: 45; label-anchor: 0.1 0.9;}";
Style style = translate(css);
Rule rule = assertSingleRule(style);
TextSymbolizer ts = assertSingleSymbolizer(rule, TextSymbolizer.class);
assertLiteral("test", ts.getLabel());
PointPlacement pp = (PointPlacement) ts.getLabelPlacement();
assertLiteral("5", pp.getDisplacement().getDisplacementX());
assertLiteral("5", pp.getDisplacement().getDisplacementY());
assertLiteral("45", pp.getRotation());
assertLiteral("0.1", pp.getAnchorPoint().getAnchorPointX());
assertLiteral("0.9", pp.getAnchorPoint().getAnchorPointY());
}
@Test
public void labelLinePlacement() throws Exception {
String css = "* { label: 'test'; label-offset: 5;}";
Style style = translate(css);
Rule rule = assertSingleRule(style);
TextSymbolizer ts = assertSingleSymbolizer(rule, TextSymbolizer.class);
assertLiteral("test", ts.getLabel());
LinePlacement lp = (LinePlacement) ts.getLabelPlacement();
assertLiteral("5", lp.getPerpendicularOffset());
}
@Test
public void labelHalo() throws Exception {
String css = "* { label: 'test'; halo-color: white; halo-radius: 3; halo-opacity: 0.8;}";
Style style = translate(css);
Rule rule = assertSingleRule(style);
TextSymbolizer ts = assertSingleSymbolizer(rule, TextSymbolizer.class);
assertLiteral("test", ts.getLabel());
Halo halo = ts.getHalo();
assertLiteral("#ffffff", halo.getFill().getColor());
assertLiteral("0.8", halo.getFill().getOpacity());
assertLiteral("3", halo.getRadius());
}
@Test
public void labelFont() throws Exception {
String css = "* { label: 'test'; font-family: 'Arial'; font-fill: blue; font-weight: normal; font-style: italic; font-size: 20;}";
Style style = translate(css);
Rule rule = assertSingleRule(style);
TextSymbolizer ts = assertSingleSymbolizer(rule, TextSymbolizer.class);
assertLiteral("test", ts.getLabel());
Font font = ts.getFont();
assertLiteral("Arial", font.getFamily().get(0));
assertLiteral("#0000ff", ts.getFill().getColor());
assertLiteral("normal", font.getWeight());
assertLiteral("italic", font.getStyle());
assertLiteral("20", font.getSize());
}
@Test
public void labelShield() throws Exception {
String css = "* { label: 'test'; shield: symbol(square);} :shield {fill:black;}";
Style style = translate(css);
Rule rule = assertSingleRule(style);
TextSymbolizer2 ts = assertSingleSymbolizer(rule, TextSymbolizer2.class);
assertLiteral("test", ts.getLabel());
Graphic g = ts.getGraphic();
assertEquals(1, g.graphicalSymbols().size());
Mark mark = (Mark) g.graphicalSymbols().get(0);
assertLiteral("square", mark.getWellKnownName());
assertLiteral("#000000", mark.getFill().getColor());
}
@Test
public void labelPriority() throws Exception {
String css = "* { label: 'test'; -gt-label-priority: [priority];}";
Style style = translate(css);
Rule rule = assertSingleRule(style);
TextSymbolizer2 ts = assertSingleSymbolizer(rule, TextSymbolizer2.class);
assertLiteral("test", ts.getLabel());
assertExpression("priority", ts.getPriority());
}
@Test
public void labelVendorOptions() throws Exception {
String css = "* { label: 'test'; -gt-label-follow-line: true;}";
Style style = translate(css);
Rule rule = assertSingleRule(style);
TextSymbolizer2 ts = assertSingleSymbolizer(rule, TextSymbolizer2.class);
assertLiteral("test", ts.getLabel());
assertEquals("true", ts.getOptions().get("followLine"));
}
@Test
public void labelMixedMode() throws Exception {
String css = "* { label: [att1]'\n('[att2]')';}";
Style style = translate(css);
Rule rule = assertSingleRule(style);
TextSymbolizer2 ts = assertSingleSymbolizer(rule, TextSymbolizer2.class);
assertExpression("concatenate(att1, '\n(', att2, ')')", ts.getLabel());
}
@Test
public void rasterBasic() throws Exception {
String css = "* { raster-channels: auto;}";
Style style = translate(css);
Rule rule = assertSingleRule(style);
RasterSymbolizer rs = assertSingleSymbolizer(rule, RasterSymbolizer.class);
assertNull(rs.getChannelSelection());
assertNull(rs.getColorMap());
}
@Test
public void rasterGammaContrastEnhancement() throws Exception {
String css = "* { raster-channels: auto; raster-contrast-enhancement: normalize; raster-gamma: 0.5;}";
Style style = translate(css);
Rule rule = assertSingleRule(style);
RasterSymbolizer rs = assertSingleSymbolizer(rule, RasterSymbolizer.class);
assertNull(rs.getChannelSelection());
assertNull(rs.getColorMap());
assertEquals(ContrastMethod.NORMALIZE, rs.getContrastEnhancement().getMethod());
assertLiteral("0.5", rs.getContrastEnhancement().getGammaValue());
}
@Test
public void rasterChannelSelection() throws Exception {
String css = "* { raster-channels: 'band1'; raster-contrast-enhancement: normalize; raster-gamma: 0.5;}";
Style style = translate(css);
Rule rule = assertSingleRule(style);
RasterSymbolizer rs = assertSingleSymbolizer(rule, RasterSymbolizer.class);
assertNull(rs.getColorMap());
SelectedChannelType grayChannel = rs.getChannelSelection().getGrayChannel();
assertNotNull(grayChannel);
assertEquals(ContrastMethod.NORMALIZE, grayChannel.getContrastEnhancement().getMethod());
assertLiteral("0.5", grayChannel.getContrastEnhancement().getGammaValue());
}
@Test
public void rasterChannelSelectionRGB() throws Exception {
String css = "* { raster-channels: 'band1' 'band5' 'band3';}";
Style style = translate(css);
Rule rule = assertSingleRule(style);
RasterSymbolizer rs = assertSingleSymbolizer(rule, RasterSymbolizer.class);
assertNull(rs.getColorMap());
SelectedChannelType[] channels = rs.getChannelSelection().getRGBChannels();
assertEquals("band1", channels[0].getChannelName());
assertEquals("band5", channels[1].getChannelName());
assertEquals("band3", channels[2].getChannelName());
}
@Test
public void rasterColorMap() throws Exception {
String css = "* { raster-channels: 'auto'; raster-color-map: color-map-entry(black, 100) color-map-entry(white, 1000) color-map-entry(red, 10000, 0);}";
Style style = translate(css);
Rule rule = assertSingleRule(style);
RasterSymbolizer rs = assertSingleSymbolizer(rule, RasterSymbolizer.class);
ColorMap cm = rs.getColorMap();
assertEquals(3, cm.getColorMapEntries().length);
assertLiteral("#000000", cm.getColorMapEntry(0).getColor());
assertLiteral("1.0", cm.getColorMapEntry(0).getOpacity());
assertLiteral("100", cm.getColorMapEntry(0).getQuantity());
assertLiteral("#ffffff", cm.getColorMapEntry(1).getColor());
assertLiteral("1.0", cm.getColorMapEntry(1).getOpacity());
assertLiteral("1000", cm.getColorMapEntry(1).getQuantity());
assertLiteral("#ff0000", cm.getColorMapEntry(2).getColor());
assertLiteral("0", cm.getColorMapEntry(2).getOpacity());
assertLiteral("10000", cm.getColorMapEntry(2).getQuantity());
}
@Test
public void multiComment() throws Exception {
String css = "/* This is an initial comment */\n" + //
"\n" + //
"/* @title This is the title */\n" + //
"* {\n" + //
" mark: symbol('circle');\n" + //
"}\n" + //
"\n" + //
"/* This is a closing comment */" //
+ "\n ";
Style style = translate(css);
Rule rule = assertSingleRule(style);
assertSingleSymbolizer(rule, PointSymbolizer.class);
}
@Test
public void scaleWithinOr() throws IOException, CQLException {
String css = "[@scale < 10000],[foo='bar'] { fill: orange; }";
// used to just blow
Style style = translate(css);
assertEquals("Expected single feature type style, found "
+ style.featureTypeStyles().size(), 1, style.featureTypeStyles().size());
FeatureTypeStyle fts = style.featureTypeStyles().get(0);
List<? extends Rule> rules = fts.rules();
assertEquals(3, rules.size());
}
@Test
public void testParseQuotedURL() throws Exception {
String css = "* { mark: url('file://BidirectionShield-High.svg');}";
Style style = translate(css);
Rule rule = assertSingleRule(style);
PointSymbolizer ps = (PointSymbolizer) rule.symbolizers().get(0);
ExternalGraphic eg = (ExternalGraphic) ps.getGraphic().graphicalSymbols().get(0);
String uri = eg.getURI();
assertEquals("file://BidirectionShield-High.svg", uri);
}
@Test
public void testEmptyStyle() throws Exception {
String css = "* { line: gray }";
try {
translate(css);
fail("Generating an empty style, should have thrown an exception");
} catch (IllegalArgumentException e) {
// fine
}
}
@Test
public void testEnvFunction() throws Exception {
String css = "[env('foo', 'default') = 'bar'] { fill: blue; }";
Style style = translate(css);
Rule rule = assertSingleRule(style);
assertEquals(ECQL.toFilter("env('foo', 'default') = 'bar'"), rule.getFilter());
}
@Test
public void testBlendPoint() throws Exception {
String css = "* { mark: symbol(circle); mark-composite: multiply;}";
Style style = translate(css);
Rule rule = assertSingleRule(style);
assertEquals(Filter.INCLUDE, rule.getFilter());
PointSymbolizer ps = assertSingleSymbolizer(rule, PointSymbolizer.class);
assertEquals(1, ps.getOptions().size());
assertEquals("multiply", ps.getOptions().get("composite"));
}
@Test
public void testBlendLine() throws Exception {
String css = "* { stroke: red; stroke-composite: multiply;}";
Style style = translate(css);
Rule rule = assertSingleRule(style);
assertEquals(Filter.INCLUDE, rule.getFilter());
LineSymbolizer ls = assertSingleSymbolizer(rule, LineSymbolizer.class);
assertEquals(1, ls.getOptions().size());
assertEquals("multiply", ls.getOptions().get("composite"));
}
@Test
public void testBlendPolygon() throws Exception {
String css = "* { fill: red; fill-composite: multiply;}";
Style style = translate(css);
Rule rule = assertSingleRule(style);
assertEquals(Filter.INCLUDE, rule.getFilter());
PolygonSymbolizer ps = assertSingleSymbolizer(rule, PolygonSymbolizer.class);
assertEquals(1, ps.getOptions().size());
assertEquals("multiply", ps.getOptions().get("composite"));
}
@Test
public void testBlendRaster() throws Exception {
String css = "* { raster-channels: auto; raster-composite : multiply; }";
Style style = translate(css);
Rule rule = assertSingleRule(style);
assertEquals(Filter.INCLUDE, rule.getFilter());
RasterSymbolizer rs = assertSingleSymbolizer(rule, RasterSymbolizer.class);
assertEquals(1, rs.getOptions().size());
assertEquals("multiply", rs.getOptions().get("composite"));
}
@Test
public void testBlendFTS() throws Exception {
String css = "* { stroke: red; composite : multiply; }";
Style style = translate(css);
// should not be in the symbolizer this time
Rule rule = assertSingleRule(style);
assertEquals(Filter.INCLUDE, rule.getFilter());
LineSymbolizer ls = assertSingleSymbolizer(rule, LineSymbolizer.class);
assertEquals(0, ls.getOptions().size());
// but in the feature type style
org.geotools.styling.FeatureTypeStyle fts = (org.geotools.styling.FeatureTypeStyle) style
.featureTypeStyles().get(0);
assertEquals(2, fts.getOptions().size());
assertEquals("multiply", fts.getOptions().get("composite"));
assertNull(fts.getOptions().get("composite-base"));
}
@Test
public void testCompositeBaseFTS() throws Exception {
String css = "* { stroke: red; composite-base : true; }";
Style style = translate(css);
// should not be in the symbolizer this time
Rule rule = assertSingleRule(style);
assertEquals(Filter.INCLUDE, rule.getFilter());
LineSymbolizer ls = assertSingleSymbolizer(rule, LineSymbolizer.class);
assertEquals(0, ls.getOptions().size());
// but in the feature type style
org.geotools.styling.FeatureTypeStyle fts = (org.geotools.styling.FeatureTypeStyle) style
.featureTypeStyles().get(0);
assertEquals("true", fts.getOptions().get("composite-base"));
assertNull(fts.getOptions().get("composite"));
}
@Test
public void testCompositeAndBaseFTS() throws Exception {
String css = "* { stroke: red; composite: multiply; composite-base : true; }";
Style style = translate(css);
// should not be in the symbolizer this time
Rule rule = assertSingleRule(style);
assertEquals(Filter.INCLUDE, rule.getFilter());
LineSymbolizer ls = assertSingleSymbolizer(rule, LineSymbolizer.class);
assertEquals(0, ls.getOptions().size());
// but in the feature type style
org.geotools.styling.FeatureTypeStyle fts = (org.geotools.styling.FeatureTypeStyle) style
.featureTypeStyles().get(0);
assertEquals("true", fts.getOptions().get("composite-base"));
assertEquals("multiply", fts.getOptions().get("composite"));
}
@Test
public void testSortBy() throws Exception {
String css = "* { stroke: red; sort-by: \"cat A, name D\"; }";
Style style = translate(css);
// should not be in the symbolizer this time
Rule rule = assertSingleRule(style);
assertEquals(Filter.INCLUDE, rule.getFilter());
LineSymbolizer ls = assertSingleSymbolizer(rule, LineSymbolizer.class);
assertEquals(0, ls.getOptions().size());
// but in the feature type style
org.geotools.styling.FeatureTypeStyle fts = (org.geotools.styling.FeatureTypeStyle) style
.featureTypeStyles().get(0);
assertEquals("cat A, name D",
fts.getOptions().get(org.geotools.styling.FeatureTypeStyle.SORT_BY));
}
@Test
public void testSortByGroup() throws Exception {
String css = "* { stroke: red; sort-by: \"cat A, name D\"; sort-by-group: \"theGroup\"}";
Style style = translate(css);
// should not be in the symbolizer this time
Rule rule = assertSingleRule(style);
assertEquals(Filter.INCLUDE, rule.getFilter());
LineSymbolizer ls = assertSingleSymbolizer(rule, LineSymbolizer.class);
assertEquals(0, ls.getOptions().size());
// but in the feature type style
org.geotools.styling.FeatureTypeStyle fts = (org.geotools.styling.FeatureTypeStyle) style
.featureTypeStyles().get(0);
assertEquals("cat A, name D",
fts.getOptions().get((org.geotools.styling.FeatureTypeStyle.SORT_BY)));
assertEquals("theGroup",
fts.getOptions().get(org.geotools.styling.FeatureTypeStyle.SORT_BY_GROUP));
}
@Test
public void testMultipleFonts() throws Exception {
String css = "* { label: \"static\"; font-family: \"Serif\" \"Sans\"; font-size: 10 15; }";
Style style = translate(css);
// should not be in the symbolizer this time
Rule rule = assertSingleRule(style);
assertEquals(Filter.INCLUDE, rule.getFilter());
TextSymbolizer ts = assertSingleSymbolizer(rule, TextSymbolizer.class);
List<Font> fonts = ts.fonts();
assertEquals(2, fonts.size());
Font f1 = fonts.get(0);
assertEquals("Serif", f1.getFamily().get(0).evaluate(null));
assertEquals("10", f1.getSize().evaluate(null));
Font f2 = fonts.get(1);
assertEquals("Sans", f2.getFamily().get(0).evaluate(null));
assertEquals("15", f2.getSize().evaluate(null));
}
@Test
public void testMultipleFontsDefaltSize() throws Exception {
String css = "* { label: \"static\"; font-family: \"Serif\" \"Sans\"}";
Style style = translate(css);
// should not be in the symbolizer this time
Rule rule = assertSingleRule(style);
assertEquals(Filter.INCLUDE, rule.getFilter());
TextSymbolizer ts = assertSingleSymbolizer(rule, TextSymbolizer.class);
List<Font> fonts = ts.fonts();
assertEquals(2, fonts.size());
Font f1 = fonts.get(0);
assertEquals("Serif", f1.getFamily().get(0).evaluate(null));
assertEquals(10, f1.getSize().evaluate(null));
Font f2 = fonts.get(1);
assertEquals("Sans", f2.getFamily().get(0).evaluate(null));
assertEquals(10, f2.getSize().evaluate(null));
}
@Test
public void testMultipleFontsSingleSize() throws Exception {
String css = "* { label: \"static\"; font-family: \"Serif\" \"Sans\"; font-size: 10 }";
Style style = translate(css);
// should not be in the symbolizer this time
Rule rule = assertSingleRule(style);
assertEquals(Filter.INCLUDE, rule.getFilter());
TextSymbolizer ts = assertSingleSymbolizer(rule, TextSymbolizer.class);
List<Font> fonts = ts.fonts();
assertEquals(2, fonts.size());
Font f1 = fonts.get(0);
assertEquals("Serif", f1.getFamily().get(0).evaluate(null));
assertEquals("10", f1.getSize().evaluate(null));
Font f2 = fonts.get(1);
assertEquals("Sans", f2.getFamily().get(0).evaluate(null));
assertEquals("10", f2.getSize().evaluate(null));
}
@Test
public void perpendicularOffset() throws Exception {
String css = "* { stroke: black, yellow; stroke-offset: 0, 5;} ";
Style style = translate(css);
Rule rule = assertSingleRule(style);
assertEquals(2, rule.symbolizers().size());
LineSymbolizer ls1 = (LineSymbolizer) rule.symbolizers().get(0);
assertNull(ls1.getPerpendicularOffset());
LineSymbolizer ls2 = (LineSymbolizer) rule.symbolizers().get(1);
assertNotNull(ls2.getPerpendicularOffset());
assertEquals(5, ls2.getPerpendicularOffset().evaluate(null, Double.class), 0);
}
@Test
public void styleTitle() throws Exception {
String css = "@styleTitle \"test title\";\n"
+ "* { mark: symbol('circle'); }";
Style style = translate(css);
assertEquals("test title", style.getDescription().getTitle().toString());
}
@Test
public void styleAbstract() throws Exception {
String css = "@styleAbstract \"style description\";\n"
+ "* { mark: symbol('circle'); }";
Style style = translate(css);
assertEquals("style description", style.getDescription().getAbstract().toString());
}
@Test
public void testModeFlat() throws CQLException, TransformerException {
String css = "@mode \"Flat\"; " +
"[value1=1] { fill: green; } " +
"[value2=2] { stroke: red; }";
Style style = translate(css);
assertEquals(1, style.featureTypeStyles().size());
assertEquals(2, style.featureTypeStyles().get(0).rules().size());
assertEquals(ECQL.toFilter("value1=1"), style.featureTypeStyles().get(0).rules().get(0).getFilter());
assertEquals(ECQL.toFilter("value2=2"), style.featureTypeStyles().get(0).rules().get(1).getFilter());
assertEquals("#008000", ((PolygonSymbolizer)style.featureTypeStyles().get(0).rules().get(0).symbolizers().get(0)).getFill().getColor().toString());
assertEquals(1, style.featureTypeStyles().get(0).rules().get(0).symbolizers().size());
assertNull(((PolygonSymbolizer)style.featureTypeStyles().get(0).rules().get(0).symbolizers().get(0)).getStroke());
assertEquals(1, style.featureTypeStyles().get(0).rules().get(1).symbolizers().size());
assertEquals("#ff0000", ((LineSymbolizer)style.featureTypeStyles().get(0).rules().get(1).symbolizers().get(0)).getStroke().getColor().toString());
}
@Test
public void testModeFlat_include() throws CQLException, TransformerException {
String css = "@mode \"Flat\"; " +
"* { fill: blue; } " +
"[value1=1] { fill: green; } " +
"[value2=2] { stroke: red; }";
Style style = translate(css);
assertEquals(1, style.featureTypeStyles().size());
assertEquals(3, style.featureTypeStyles().get(0).rules().size());
assertEquals(ECQL.toFilter("include"), style.featureTypeStyles().get(0).rules().get(0).getFilter());
assertEquals(ECQL.toFilter("value1=1"), style.featureTypeStyles().get(0).rules().get(1).getFilter());
assertEquals(ECQL.toFilter("value2=2"), style.featureTypeStyles().get(0).rules().get(2).getFilter());
}
@Test
public void testModeFlat_pseudoRules() throws Exception {
String css = "@mode \"Flat\"; " + "* { fill: symbol('circle');} :fill { stroke: black;}";
Style style = translate(css);
Rule rule = assertSingleRule(style);
PolygonSymbolizer ps = assertSingleSymbolizer(rule, PolygonSymbolizer.class);
Fill fill = ps.getFill();
Graphic gf = fill.getGraphicFill();
assertEquals(1, gf.graphicalSymbols().size());
Mark mark = (Mark) gf.graphicalSymbols().get(0);
assertLiteral("circle", mark.getWellKnownName());
assertNull(mark.getFill());
assertLiteral("#000000", mark.getStroke().getColor());
assertNull(gf.getSize());
assertLiteral("1.0", gf.getOpacity());
assertLiteral("0", gf.getRotation());
}
@Test
public void testModeFlat1() throws CQLException, TransformerException {
String css = "@mode \"Flat\"; " +
"[value1=1] { stroke: green; stroke-width:2px;}"
+ "[value2=2] { stroke: green; stroke-width:2px;}"
+ "[value1=1] { stroke: blue; stroke-width:10px;}";
Style style = translate(css);
assertEquals(3, style.featureTypeStyles().get(0).rules().size());
assertEquals(ECQL.toFilter("value1=1"), style.featureTypeStyles().get(0).rules().get(0).getFilter());
assertEquals(ECQL.toFilter("value2=2"), style.featureTypeStyles().get(0).rules().get(1).getFilter());
assertEquals(ECQL.toFilter("value1=1"), style.featureTypeStyles().get(0).rules().get(2).getFilter());
assertEquals("2", ((LineSymbolizer)style.featureTypeStyles().get(0).rules().get(0).symbolizers().get(0)).getStroke().getWidth().toString());
assertEquals("#008000", ((LineSymbolizer)style.featureTypeStyles().get(0).rules().get(0).symbolizers().get(0)).getStroke().getColor().toString());
assertEquals("2", ((LineSymbolizer)style.featureTypeStyles().get(0).rules().get(1).symbolizers().get(0)).getStroke().getWidth().toString());
assertEquals("#008000", ((LineSymbolizer)style.featureTypeStyles().get(0).rules().get(1).symbolizers().get(0)).getStroke().getColor().toString());
assertEquals("10", ((LineSymbolizer)style.featureTypeStyles().get(0).rules().get(2).symbolizers().get(0)).getStroke().getWidth().toString());
assertEquals("#0000ff", ((LineSymbolizer)style.featureTypeStyles().get(0).rules().get(2).symbolizers().get(0)).getStroke().getColor().toString());
// printStyle(style);
}
private void printStyle(Style style) throws TransformerException {
SLDTransformer transformer = new SLDTransformer();
String xml = transformer.transform( style );
System.out.println(xml);
}
@Test
public void testModeFlat1_1() throws CQLException, TransformerException {
String css = "@mode \"Flat\"; " +
"[value1=1] { stroke: green; stroke-width:2px;z-index:1;}"
+ "[value2=2] { stroke: green; stroke-width:2px;z-index:2;}"
+ "[value1=1] { stroke: blue; stroke-width:10px;z-index:3;}";
Style style = translate(css);
assertEquals(1, style.featureTypeStyles().get(0).rules().size());
assertEquals(ECQL.toFilter("value1=1"), style.featureTypeStyles().get(0).rules().get(0).getFilter());
assertEquals(ECQL.toFilter("value2=2"), style.featureTypeStyles().get(1).rules().get(0).getFilter());
assertEquals(ECQL.toFilter("value1=1"), style.featureTypeStyles().get(2).rules().get(0).getFilter());
assertEquals("2", ((LineSymbolizer)style.featureTypeStyles().get(0).rules().get(0).symbolizers().get(0)).getStroke().getWidth().toString());
assertEquals("#008000", ((LineSymbolizer)style.featureTypeStyles().get(0).rules().get(0).symbolizers().get(0)).getStroke().getColor().toString());
assertEquals("2", ((LineSymbolizer)style.featureTypeStyles().get(1).rules().get(0).symbolizers().get(0)).getStroke().getWidth().toString());
assertEquals("#008000", ((LineSymbolizer)style.featureTypeStyles().get(1).rules().get(0).symbolizers().get(0)).getStroke().getColor().toString());
assertEquals("10", ((LineSymbolizer)style.featureTypeStyles().get(2).rules().get(0).symbolizers().get(0)).getStroke().getWidth().toString());
assertEquals("#0000ff", ((LineSymbolizer)style.featureTypeStyles().get(2).rules().get(0).symbolizers().get(0)).getStroke().getColor().toString());
// printStyle(style);
}
@Test
public void testModeFlat2_mark() throws Exception {
String css = "@mode \"Flat\"; " +
"[value1=1] { mark: symbol(circle); } [value1=1] :mark { fill: green; } [value1=1] [value2=2] :mark { fill: blue; }";
Style style = translate(css);
Rule rule = assertSingleRule(style);
PointSymbolizer ps = assertSingleSymbolizer(rule, PointSymbolizer.class);
Graphic g = ps.getGraphic();
Mark mark = (Mark) g.graphicalSymbols().get(0);
assertLiteral("circle", mark.getWellKnownName());
assertLiteral("#008000", mark.getFill().getColor());
}
@Test
public void testModeFlat3_mark() throws CQLException, TransformerException {
String css = "@mode \"Flat\"; " +
"* { fill: blue; } " +
"[value1=1] { fill: green; } " +
"[value2=2] { stroke: red; } " +
"[value3=3] { mark: symbol(circle); mark-size: 10; mark-rotation: 45; mark-geometry: [centroid(the_geom)];} [value3=3] :mark { fill: blue; }";
Style style = translate(css);
assertEquals(1, style.featureTypeStyles().size());
assertEquals(4, style.featureTypeStyles().get(0).rules().size());
assertEquals(ECQL.toFilter("include"), style.featureTypeStyles().get(0).rules().get(0).getFilter());
assertEquals(ECQL.toFilter("value1=1"), style.featureTypeStyles().get(0).rules().get(1).getFilter());
assertEquals(ECQL.toFilter("value2=2"), style.featureTypeStyles().get(0).rules().get(2).getFilter());
assertEquals(ECQL.toFilter("value3=3"), style.featureTypeStyles().get(0).rules().get(3).getFilter());
PointSymbolizer ps = assertSingleSymbolizer(style.featureTypeStyles().get(0).rules().get(3), PointSymbolizer.class);
assertExpression("centroid(the_geom)", ps.getGeometry());
Graphic g = ps.getGraphic();
assertLiteral("10", g.getSize());
assertLiteral("45", g.getRotation());
assertNotNull(g);
assertEquals(1, g.graphicalSymbols().size());
Mark mark = (Mark) g.graphicalSymbols().get(0);
assertLiteral("circle", mark.getWellKnownName());
assertLiteral("#0000ff", mark.getFill().getColor());
// printStyle(style);
}
@Test
public void testModeFlat4_multiValued() throws CQLException, TransformerException {
String css = "@mode \"Flat\"; " +
"[value1=1] { fill: green, red; }";
Style style = translate(css);
assertEquals(1, style.featureTypeStyles().size());
assertEquals(1, style.featureTypeStyles().get(0).rules().size());
assertEquals(ECQL.toFilter("value1=1"), style.featureTypeStyles().get(0).rules().get(0).getFilter());
assertEquals(2, style.featureTypeStyles().get(0).rules().get(0).symbolizers().size());
// printStyle(style);
}
@Test
public void testModeFlat5_mark() throws Exception {
String css = "@mode \"Flat\"; " +
"* { mark: symbol(circle); mark-size: 10; mark-rotation: 45; mark-geometry: [centroid(the_geom)];} :mark { fill: blue; }";
Style style = translate(css);
Rule rule = assertSingleRule(style);
PointSymbolizer ps = assertSingleSymbolizer(rule, PointSymbolizer.class);
assertExpression("centroid(the_geom)", ps.getGeometry());
Graphic g = ps.getGraphic();
assertLiteral("10", g.getSize());
assertLiteral("45", g.getRotation());
assertNotNull(g);
assertEquals(1, g.graphicalSymbols().size());
Mark mark = (Mark) g.graphicalSymbols().get(0);
assertLiteral("circle", mark.getWellKnownName());
assertLiteral("#0000ff", mark.getFill().getColor());
}
@Test
public void testSimpleTransform() throws Exception {
String css = "* { transform: ras:Contour(levels: 1100 1200 1300); stroke: black}";
Style style = translate(css);
// check transformation
FeatureTypeStyle fts = assertSingleFeatureTypeStyle(style);
Function tx = assertTransformation(fts);
assertContour123(tx);
}
@Test
public void testTwoLevelTransform() throws Exception {
String css = "* { transform: ras:Contour(levels: 1100 1200 1300); stroke: black; z-index: 0}\n"
+ "* { transform: ras:RasterAsPointCollection(); mark: symbol('square'); z-index: 1}";
Style style = translate(css);
assertEquals(2, style.featureTypeStyles().size());
// base level, contour
FeatureTypeStyle fts1 = style.featureTypeStyles().get(0);
Function tx1 = assertTransformation(fts1);
assertContour123(tx1);
// second level, raster as point collection
FeatureTypeStyle fts2 = style.featureTypeStyles().get(1);
Function tx2 = assertTransformation(fts2);
assertEquals("ras:RasterAsPointCollection", tx2.getName());
List<Expression> expressions = tx2.getParameters();
assertEquals(1, expressions.size());
assertParameterFunction(expressions.get(0), "data", 0);
}
@Test
public void testPerpendicularOffsetExpression() throws Exception {
String css = "* { stroke: red; stroke-offset: [a * 2]; }";
Style style = translate(css);
Rule rule = assertSingleRule(style);
LineSymbolizer ls = assertSingleSymbolizer(rule, LineSymbolizer.class);
assertEquals(ECQL.toExpression("a * 2"), ls.getPerpendicularOffset());
}
@Test
public void testDarkenAsCQL() throws Exception {
String css = "* { fill: [darken('#FF0000', '30%')]}";
Style style = translate(css);
Rule rule = assertSingleRule(style);
PolygonSymbolizer ps = assertSingleSymbolizer(rule, PolygonSymbolizer.class);
final Expression color = ps.getFill().getColor();
assertThat(color, instanceOf(DarkenFunction.class));
assertEquals(Color.decode("#660000"), color.evaluate(null, Color.class));
}
@Test
public void testDarkenAsFunction() throws Exception {
String css = "* { fill: darken(red, 30%);}";
Style style = translate(css);
Rule rule = assertSingleRule(style);
PolygonSymbolizer ps = assertSingleSymbolizer(rule, PolygonSymbolizer.class);
final Expression color = ps.getFill().getColor();
assertThat(color, instanceOf(DarkenFunction.class));
assertEquals(Color.decode("#660000"), color.evaluate(null, Color.class));
}
private Function assertTransformation(FeatureTypeStyle fts) {
Expression ex = fts.getTransformation();
assertNotNull(ex);
assertThat(ex, instanceOf(Function.class));
Function tx = (Function) ex;
return tx;
}
private void assertContour123(Function tx) {
assertEquals("ras:Contour", tx.getName());
List<Expression> expressions = tx.getParameters();
assertEquals(2, expressions.size());
assertParameterFunction(expressions.get(0), "data", 0);
Function p2 = assertParameterFunction(expressions.get(1), "levels", 3);
List<Expression> p2Params = p2.getParameters();
assertEquals("1100", p2Params.get(1).evaluate(null));
assertEquals("1200", p2Params.get(2).evaluate(null));
assertEquals("1300", p2Params.get(3).evaluate(null));
}
private Function assertParameterFunction(Expression expression, String expectedKey, int expectedValueCount) {
assertThat(expression, instanceOf(Function.class));
Function f = (Function) expression;
assertEquals("parameter", f.getName());
final List<Expression> parameters = f.getParameters();
assertTrue("At least one parameter, the key", parameters.size() > 0);
assertEquals(expectedKey, parameters.get(0).evaluate(null));
assertEquals(expectedValueCount, parameters.size() - 1);
return f;
}
}