/*
* Copyright (c) 2017 Cisco Systems, Inc. and others. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/epl-v10.html
*/
package org.opendaylight.yangtools.yang.parser.stmt.rfc6020;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import com.google.common.collect.ImmutableList;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.io.UnsupportedEncodingException;
import java.util.List;
import org.junit.Test;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
import org.opendaylight.yangtools.yang.model.api.type.PatternConstraint;
import org.opendaylight.yangtools.yang.model.api.type.StringTypeDefinition;
import org.opendaylight.yangtools.yang.model.util.RegexUtils;
import org.opendaylight.yangtools.yang.stmt.StmtTestUtils;
public class Bug5410Test {
private static final String FOO_NS = "foo";
private static final String FOO_REV = "1970-01-01";
@Test
public void testJavaRegexFromXSD() {
testPattern("^[^:]+$", "^\\^[^:]+\\$$", ImmutableList.of("^a$", "^abc$"),
ImmutableList.of("abc$", "^abc", "^a:bc$"));
testPattern("^[$^]$", "^\\^[$^]\\$$", ImmutableList.of("^^$", "^$$"), ImmutableList.of("^^", "^$", "$^", "$$"));
testPattern("[$-%]+", "^[$-%]+$", ImmutableList.of("$", "%", "%$"), ImmutableList.of("$-", "$-%", "-", "^"));
testPattern("[$-&]+", "^[$-&]+$", ImmutableList.of("$", "%&", "%$", "$%&"), ImmutableList.of("#", "$-&", "'"));
testPattern("[a-z&&[^m-p]]+", "^[a-z&&[^m-p]]+$", ImmutableList.of("a", "z", "az"),
ImmutableList.of("m", "anz", "o"));
testPattern("^[\\[-b&&[^^-a]]+$", "^\\^[\\[-b&&[^^-a]]+\\$$", ImmutableList.of("^[$", "^\\$", "^]$", "^b$"),
ImmutableList.of("^a$", "^^$", "^_$"));
testPattern("[^^-~&&[^$-^]]", "^[^^-~&&[^$-^]]$", ImmutableList.of("!", "\"", "#"),
ImmutableList.of("a", "A", "z", "Z", "$", "%", "^", "}"));
testPattern("\\\\\\[^[^^-~&&[^$-^]]", "^\\\\\\[\\^[^^-~&&[^$-^]]$",
ImmutableList.of("\\[^ ", "\\[^!", "\\[^\"", "\\[^#"),
ImmutableList.of("\\[^a", "\\[^A", "\\[^z", "\\[^Z", "\\[^$", "\\[^%", "\\[^^", "\\[^}"));
testPattern("^\\[^\\\\[^^-b&&[^\\[-\\]]]\\]^", "^\\^\\[\\^\\\\[^^-b&&[^\\[-\\]]]\\]\\^$",
ImmutableList.of("^[^\\c]^", "^[^\\Z]^"),
ImmutableList.of("^[^\\[]^", "^[^\\\\]^", "^[^\\]]^", "^[^\\^]^", "^[^\\_]^", "^[^\\b]^"));
testPattern("[\\^]$", "^[\\^]\\$$", ImmutableList.of("^$"),
ImmutableList.of("^", "$", "$^", "\\", "\\^", "\\^\\", "\\^\\$"));
}
@Test
public void testInvalidXSDRegexes() throws UnsupportedEncodingException {
testInvalidPattern("$^a^[$^\\]", "Unclosed character class");
testInvalidPattern("$(\\)", "Unclosed group");
}
@Test
public void testJavaPattern() {
testPattern("^[$^]+$", ImmutableList.of("$^", "^", "$"), ImmutableList.of("\\", "a"));
testPattern("^[^$-^]$", ImmutableList.of("a", "_", "#"), ImmutableList.of("%", "^", "$", "]", "\\"));
}
@Test
public void testYangPattern() throws Exception {
final SchemaContext context = StmtTestUtils.parseYangSources("/bugs/bug5410");
assertNotNull(context);
final PatternConstraint pattern = getPatternConstraintOf(context, "leaf-with-pattern");
final String rawRegex = pattern.getRawRegularExpression();
final String expectedYangRegex = "$0$.*|$1$[a-zA-Z0-9./]{1,8}$[a-zA-Z0-9./]{22}|$5$(rounds=\\d+$)?[a-zA-Z0-9./]{1,16}$[a-zA-Z0-9./]{43}|$6$(rounds=\\d+$)?[a-zA-Z0-9./]{1,16}$[a-zA-Z0-9./]{86}";
assertEquals(expectedYangRegex, rawRegex);
final String javaRegexFromYang = pattern.getRegularExpression();
final String expectedJavaRegex = "^\\$0\\$.*|\\$1\\$[a-zA-Z0-9./]{1,8}\\$[a-zA-Z0-9./]{22}|\\$5\\$(rounds=\\d+\\$)?[a-zA-Z0-9./]{1,16}\\$[a-zA-Z0-9./]{43}|\\$6\\$(rounds=\\d+\\$)?[a-zA-Z0-9./]{1,16}\\$[a-zA-Z0-9./]{86}$";
assertEquals(expectedJavaRegex, javaRegexFromYang);
final String value = "$6$AnrKGc0V$B/0/A.pWg4HrrA6YiEJOtFGibQ9Fmm5.4rI/00gEz3QeB7joSxBU3YtbHDm6NSkS1dKTQy3BWhwKKDS8nB5S//";
testPattern(javaRegexFromYang, ImmutableList.of(value), ImmutableList.of());
}
@Test
public void testCaret() {
testPattern("^", "\\^");
}
@Test
public void testTextCaret() {
testPattern("abc^", "abc\\^");
}
@Test
public void testTextDollar() {
testPattern("abc$", "abc\\$");
}
@Test
public void testCaretCaret() {
testPattern("^^", "\\^\\^");
}
@Test
public void testCaretDollar() {
testPattern("^$", "\\^\\$");
}
@Test
public void testDot() {
testPattern(".", ".");
}
@Test
public void testNotColon() {
testPattern("[^:]+", "[^:]+");
}
@Test
public void testDollar() {
testPattern("$", "\\$");
}
@Test
public void testDollarOneDollar() {
testPattern("$1$", "\\$1\\$");
}
@Test
public void testDollarPercentRange() {
testPattern("[$-%]+", "[$-%]+");
}
@Test
public void testDollarRange() {
testPattern("[$$]+", "[$$]+");
}
@Test
public void testDollarCaretRange() {
testPattern("[$^]+", "[$^]+");
}
@Test
public void testSimple() {
testPattern("abc", "abc");
}
@Test
public void testDotPlus() {
testPattern(".+", ".+");
}
@Test
public void testDotStar() {
testPattern(".*", ".*");
}
@Test
public void testSimpleOptional() {
testPattern("a?", "a?");
}
@Test
public void testRangeOptional() {
testPattern("[a-z]?", "[a-z]?");
}
private static void testPattern(final String xsdRegex, final String expectedJavaRegex,
final List<String> positiveMatches, final List<String> negativeMatches) {
final String javaRegexFromXSD = javaRegexFromXSD(xsdRegex);
assertEquals(expectedJavaRegex, javaRegexFromXSD);
for (final String value : positiveMatches) {
assertTrue("Value '" + value + "' does not match java regex '" + javaRegexFromXSD + "'",
testMatch(javaRegexFromXSD, value));
}
for (final String value : negativeMatches) {
assertFalse("Value '" + value + "' matches java regex '" + javaRegexFromXSD + "'",
testMatch(javaRegexFromXSD, value));
}
}
private static void testPattern(final String javaRegex, final List<String> positiveMatches,
final List<String> negativeMatches) {
for (final String value : positiveMatches) {
assertTrue("Value '" + value + "' does not match java regex '" + javaRegex + "'",
testMatch(javaRegex, value));
}
for (final String value : negativeMatches) {
assertFalse("Value '" + value + "' matches java regex '" + javaRegex + "'", testMatch(javaRegex, value));
}
}
private static String javaRegexFromXSD(final String xsdRegex) {
return RegexUtils.getJavaRegexFromXSD(xsdRegex);
}
private static boolean testMatch(final String javaRegex, final String value) {
return value.matches(javaRegex);
}
private static void testPattern(final String xsdRegex, final String unanchoredJavaRegex) {
testPattern(xsdRegex, '^' + unanchoredJavaRegex + '$', ImmutableList.of(), ImmutableList.of());
}
private static PatternConstraint getPatternConstraintOf(final SchemaContext context, final String leafName) {
final DataSchemaNode dataChildByName = context.getDataChildByName(foo(leafName));
assertTrue(dataChildByName instanceof LeafSchemaNode);
final LeafSchemaNode leaf = (LeafSchemaNode) dataChildByName;
final TypeDefinition<? extends TypeDefinition<?>> type = leaf.getType();
assertTrue(type instanceof StringTypeDefinition);
final StringTypeDefinition strType = (StringTypeDefinition) type;
return strType.getPatternConstraints().iterator().next();
}
private static QName foo(final String localName) {
return QName.create(FOO_NS, FOO_REV, localName);
}
private static void testInvalidPattern(final String xsdRegex, final String expectedMessage) throws UnsupportedEncodingException {
final PrintStream stdout = System.out;
final ByteArrayOutputStream output = new ByteArrayOutputStream();
System.setOut(new PrintStream(output, true, "UTF-8"));
javaRegexFromXSD(xsdRegex);
final String testLog = output.toString();
assertTrue(testLog.contains(expectedMessage));
System.setOut(stdout);
}
}