/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2016 Open Source Geospatial Foundation (OSGeo)
* (C) 2014-2016 Boundless Spatial
*
* 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.ysld;
import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.Matchers.anyOf;
import static org.hamcrest.Matchers.anything;
import static org.hamcrest.Matchers.describedAs;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.greaterThan;
import static org.hamcrest.Matchers.hasItems;
import static org.hamcrest.Matchers.hasProperty;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.lessThanOrEqualTo;
import static org.hamcrest.Matchers.nullValue;
import java.awt.Color;
import java.util.Arrays;
import java.util.regex.Pattern;
import org.geotools.styling.Rule;
import org.geotools.ysld.parse.ScaleRange;
import org.hamcrest.BaseMatcher;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.hamcrest.Matchers;
import org.opengis.filter.expression.Expression;
import org.opengis.filter.expression.Function;
import org.opengis.filter.expression.Literal;
import org.opengis.filter.expression.NilExpression;
import org.opengis.filter.expression.PropertyName;
public enum TestUtils {
;
/**
* Matches a rule if it applies to a given scale
*
* @param scale denominator of the scale
*/
public static Matcher<Rule> appliesToScale(double scale) {
return describedAs("rule applies to scale denom %0", allOf(
Matchers.<Rule> hasProperty("maxScaleDenominator", greaterThan(scale)),
Matchers.<Rule> hasProperty("minScaleDenominator", lessThanOrEqualTo(scale))),
scale);
}
public static Matcher<ScaleRange> rangeContains(double scale) {
return describedAs("scale range that contains 1:%0",
allOf(Matchers.<ScaleRange> hasProperty("maxDenom", greaterThan(scale)),
Matchers.<ScaleRange> hasProperty("minDenom", lessThanOrEqualTo(scale))),
scale);
}
/**
* Matches a Literal expression with a value matching m
*
* @param m
* @return
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public static Matcher<Expression> literal(Matcher m) {
return (Matcher) allOf(instanceOf(Literal.class), hasProperty("value", m));
}
/**
* Matches a Literal expression with a value matching o
*
* @param m
* @return
*/
public static Matcher<Expression> literal(Object o) {
return literal(lexEqualTo(o));
}
/**
* Matches a nil expression or null.
*
* @return
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public static Matcher<Expression> nilExpression() {
return (Matcher) Matchers.anyOf(nullValue(), instanceOf(NilExpression.class));
}
/**
* Matches as an attribute expression for a named attribute.
*
* @param name
* @return
*/
public static Matcher<Expression> attribute(String name) {
return Matchers.<Expression> allOf(Matchers.<Expression> instanceOf(PropertyName.class),
Matchers.<Expression> hasProperty("propertyName", equalTo(name)));
}
/**
* Matches a function with the given name and parameters matching the given matchers.
*
* @param name
* @param parameters
* @return
*/
@SafeVarargs
public static Matcher<Expression> function(String name, Matcher<Expression>... parameters) {
return function(name, hasItems(parameters));
}
/**
* Matches a function with the given name and a parameter list matching the given matcher.
*
* @param name
* @param parameters
* @return
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public static Matcher<Expression> function(String name,
Matcher<? extends Iterable<Expression>> parameters) {
return (Matcher) allOf(instanceOf(Function.class),
hasProperty("functionName", hasProperty("name", equalTo(name))),
hasProperty("parameters", parameters));
}
/**
* Compares the string representation of the object being matched to that of value.
*
* @param value
* @return
*/
public static Matcher<? extends Object> lexEqualTo(final Object value) {
return new BaseMatcher<Object>() {
@Override
public boolean matches(Object arg0) {
if (arg0 == null) {
arg0 = "";
}
return arg0.toString().equals(value.toString());
}
@Override
public void describeTo(Description arg0) {
arg0.appendText("lexicaly equal to ").appendValue(value.toString());
}
};
}
/**
* Converts a Number to double, otherwise converts to string and then parses as double then matches to the given value.
*
* @param value
* @return
*/
public static Matcher<? extends Object> numEqualTo(final double value, final double epsilon) {
return new BaseMatcher<Object>() {
@Override
public boolean matches(Object obj) {
double num;
if (obj instanceof Number) {
num = ((Number) obj).doubleValue();
} else {
num = Double.parseDouble(obj.toString());
}
return Math.abs(num - value) < epsilon;
}
@Override
public void describeTo(Description arg0) {
arg0.appendText("can be parsed as ").appendValue(value);
arg0.appendText(" to within ").appendValue(epsilon);
}
};
}
/**
* Converts a Number to long, otherwise converts to string and then parses as double then matches to the given value.
*
* @param value
* @return
*/
public static Matcher<? extends Object> numEqualTo(final long value) {
return new BaseMatcher<Object>() {
@Override
public boolean matches(Object obj) {
double num;
if (obj instanceof Number) {
num = ((Number) obj).longValue();
} else {
num = Long.parseLong(obj.toString());
}
return num == value;
}
@Override
public void describeTo(Description arg0) {
arg0.appendText("can be parsed as ").appendValue(value);
}
};
}
public static Matcher<String> asHexInt(final Matcher<Integer> m) {
return new BaseMatcher<String>() {
@Override
public boolean matches(Object arg0) {
return m.matches(Integer.parseInt((String) arg0, 16));
}
@Override
public void describeTo(Description arg0) {
arg0.appendText("Hexadecimal string ").appendDescriptionOf(m);
}
};
}
public static Matcher<String> asColor(final Matcher<Color> m) {
return new BaseMatcher<String>() {
@Override
public boolean matches(Object arg0) {
Color c;
try {
c = Color.decode((String) arg0);
} catch (NumberFormatException ex) {
c = new Color(Integer.parseInt((String) arg0, 16));
}
return m.matches(c);
}
@Override
public void describeTo(Description arg0) {
arg0.appendText("represents colour ").appendDescriptionOf(m);
}
};
}
@SuppressWarnings({ "unchecked", "rawtypes" })
public static Matcher<Object> isColor(Color c) {
String hex = String.format("#%06x", c.getRGB() & 0x00FFFFFF);
return Matchers.describedAs("is the colour %0 %1", anyOf(
(Matcher) allOf(instanceOf(String.class), asColor(equalTo(c))),
(Matcher) allOf(instanceOf(Color.class), equalTo(c)),
(Matcher) allOf(instanceOf(Integer.class), equalTo(c.getRGB() & 0x00FFFFFF))), hex,
c);
}
public static Matcher<Object> isColor(String s) {
Color c;
c = new Color(Integer.parseInt(s, 16));
return isColor(c);
}
/**
* Matches a YamlSeq where the specified entry matching the given matcher
*
* @param i
* @param m
* @return
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
public static Matcher<Object> yHasItem(final int i, final Matcher<? extends Object> m) {
return new BaseMatcher() {
@Override
public boolean matches(Object obj) {
if (!(obj instanceof YamlSeq))
return false;
YamlSeq seq = (YamlSeq) obj;
Object value = null;
try {
value = seq.map(i);
} catch (IllegalArgumentException ex1) {
try {
value = seq.seq(i);
} catch (IllegalArgumentException ex2) {
value = seq.get(i);
}
}
return (m.matches(value));
}
@Override
public void describeTo(Description desc) {
desc.appendText("YamlSeq with item ").appendValue(i).appendText(" that ")
.appendDescriptionOf(m);
}
};
}
/**
* Matches a YamlMap with an entry as named that has a value which matches the given matcher
*
* @param key
* @param m
* @return
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
public static Matcher<Object> yHasEntry(final String key, final Matcher<? extends Object> m) {
return new BaseMatcher() {
@Override
public boolean matches(Object obj) {
if (!(obj instanceof YamlMap))
return false;
YamlMap map = (YamlMap) obj;
if (!map.has(key))
return false;
Object value = null;
try {
value = map.map(key);
} catch (IllegalArgumentException ex1) {
try {
value = map.seq(key);
} catch (IllegalArgumentException ex2) {
value = map.get(key);
}
}
return (m.matches(value));
}
@Override
public void describeTo(Description desc) {
desc.appendText("YamlMap with entry ").appendValue(key).appendText(" and value ")
.appendDescriptionOf(m);
}
};
}
/**
* Matches a YamlMap with an entry as named that has a value which matches the given matcher
*
* @param key
* @return
*/
public static Matcher<Object> yHasEntry(final String key) {
return yHasEntry(key, Matchers.any(Object.class));
}
private static final Pattern TUPLE_STRIP = Pattern.compile("^\\s*\\(([^)]*)\\)\\s*$");
private static final Pattern TUPLE_SPLIT = Pattern.compile("\\s*,\\s*");
/**
* Matches a YamlSeq
*
* @param matchers
* @return
*/
@SafeVarargs
@SuppressWarnings({ "rawtypes", "unchecked" })
public static Matcher<? extends Object> yContains(final Matcher<? extends Object>... matchers) {
return new BaseMatcher() {
@Override
public boolean matches(Object obj) {
YamlSeq seq;
if (obj instanceof YamlSeq) {
seq = (YamlSeq) obj;
} else {
return false;
}
if (seq.raw().size() != matchers.length) {
return false;
}
for (int i = 0; i < matchers.length; i++) {
if (!matchers[i].matches(seq.get(i))) {
return false;
}
}
return true;
}
@Override
public void describeTo(Description desc) {
desc.appendList("Yaml Sequence with values [", ", ", "]", Arrays.asList(matchers));
}
};
}
/**
* Matches a YSLD Tuple with values matching the given matchers.
*
* @param matchers
* @return
*/
@SafeVarargs
public static Matcher<? extends Object> yTuple(final Matcher<? extends Object>... matchers) {
return yContains(matchers);
}
/**
* Matches a YSLD Tuple with n values
*
* @param n
* @return
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public static Matcher<Object> yTuple(int n) {
Matcher[] matchers = new Matcher[n];
Arrays.fill(matchers, anything());
return Matchers.describedAs("A YSLD Tuple with %0 values", (Matcher) yTuple(matchers), n);
}
/**
* For apparent consistency to the user, some values are wrapped in fake YAML strings.
*
* @param m
* @return
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
public static Matcher<? extends Object> fakeString(final Matcher<? extends Object> m) {
return new BaseMatcher() {
@Override
public boolean matches(Object obj) {
if (obj instanceof String) {
String str = (String) obj;
if (str.startsWith("'") && str.endsWith("'")) {
str = str.substring(1, str.length() - 1);
} else if (str.startsWith("\"") && str.endsWith("\"")) {
str = str.substring(1, str.length() - 1);
}
return m.matches(str);
} else {
return false;
}
}
@Override
public void describeTo(Description desc) {
desc.appendText("a fake YAML string ").appendDescriptionOf(m);
}
};
}
/**
* For apparent consistency to the user, some values are wrapped in fake YAML strings.
*
* @param m
* @return
*/
public static Matcher<? extends Object> fakeString(final String s) {
return fakeString(equalTo(s));
}
}