import javax.annotation.Nonnull;
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
import org.opendaylight.yangtools.yang.data.api.schema.xpath.XPathDocument;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
final class JaxenDocument implements XPathDocument {
private final NormalizedNode<?, ?> root;
+ private final SchemaContext context;
JaxenDocument(final JaxenSchemaContext context, final NormalizedNode<?, ?> root) {
this.root = Preconditions.checkNotNull(root);
+ this.context = context.getSchemaContext();
}
@Nonnull
public NormalizedNode<?, ?> getRootNode() {
return root;
}
+
+ @Nonnull
+ SchemaContext getSchemaContext() {
+ return context;
+ }
}
public XPathDocument createDocument(@Nonnull final NormalizedNode<?, ?> documentRoot) {
return new JaxenDocument(this, documentRoot);
}
+
+ @Nonnull
+ SchemaContext getSchemaContext() {
+ return context;
+ }
}
--- /dev/null
+/*
+ * 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.data.jaxen;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.CharMatcher;
+import com.google.common.base.Optional;
+import com.google.common.base.Splitter;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import javax.annotation.RegEx;
+import org.opendaylight.yangtools.concepts.Builder;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.ModuleImport;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.TypedSchemaNode;
+
+@Beta
+final class LeafrefXPathStringParsingPathArgumentBuilder implements Builder<List<PathArgument>> {
+
+ private static final String UP_ONE_LEVEL = "..";
+ private static final String CURRENT_FUNCTION_INVOCATION_STR = "current()";
+
+ @RegEx
+ private static final String NODE_IDENTIFIER_STR = "([A-Za-z_][A-Za-z0-9_\\.-]*:)?([A-Za-z_][A-Za-z0-9_\\.-]*)";
+
+ /**
+ * Pattern matching node-identifier YANG ABNF token
+ */
+ private static final Pattern NODE_IDENTIFIER_PATTERN = Pattern.compile(NODE_IDENTIFIER_STR);
+
+ /**
+ * Matcher matching WSP YANG ABNF token
+ *
+ */
+ private static final CharMatcher WSP = CharMatcher.anyOf(" \t");
+
+ /**
+ * Matcher matching IDENTIFIER first char token.
+ *
+ */
+ private static final CharMatcher IDENTIFIER_FIRST_CHAR = CharMatcher.inRange('a', 'z')
+ .or(CharMatcher.inRange('A', 'Z')).or(CharMatcher.is('_')).precomputed();
+ /**
+ *
+ * Matcher matching IDENTIFIER token
+ *
+ */
+ private static final CharMatcher IDENTIFIER = IDENTIFIER_FIRST_CHAR.or(CharMatcher.inRange('0', '9'))
+ .or(CharMatcher.anyOf(".-")).precomputed();
+
+ private static final Splitter SLASH_SPLITTER = Splitter.on('/');
+
+ private static final char SLASH = '/';
+ private static final char COLON = ':';
+ private static final char EQUALS = '=';
+ private static final char PRECONDITION_START = '[';
+ private static final char PRECONDITION_END = ']';
+
+ private final String xPathString;
+ private final SchemaContext schemaContext;
+ private final TypedSchemaNode schemaNode;
+ private final NormalizedNodeContext currentNodeCtx;
+ private final List<PathArgument> product = new ArrayList<>();
+
+ private int offset = 0;
+
+ LeafrefXPathStringParsingPathArgumentBuilder(final String xPathString, final SchemaContext schemaContext,
+ final TypedSchemaNode schemaNode, final NormalizedNodeContext currentNodeCtx) {
+ this.xPathString = xPathString;
+ this.schemaContext = schemaContext;
+ this.schemaNode = schemaNode;
+ this.currentNodeCtx = currentNodeCtx;
+ }
+
+ @Override
+ public List<PathArgument> build() {
+ while (!allCharactersConsumed()) {
+ product.add(computeNextArgument());
+ }
+ return ImmutableList.copyOf(product);
+ }
+
+ private PathArgument computeNextArgument() {
+ checkValid(SLASH == currentChar(), "Identifier must start with '/'.");
+ skipCurrentChar();
+ checkValid(!allCharactersConsumed(), "Identifier cannot end with '/'.");
+
+ final QName name = nextQName();
+ if (allCharactersConsumed() || SLASH == currentChar()) {
+ return new NodeIdentifier(name);
+ } else {
+ checkValid(PRECONDITION_START == currentChar(), "Last element must be identifier, predicate or '/'");
+ return computeIdentifierWithPredicate(name);
+ }
+ }
+
+ private PathArgument computeIdentifierWithPredicate(final QName name) {
+ product.add(new NodeIdentifier(name));
+
+ ImmutableMap.Builder<QName, Object> keyValues = ImmutableMap.builder();
+ while (!allCharactersConsumed() && PRECONDITION_START == currentChar()) {
+ skipCurrentChar();
+ skipWhitespaces();
+ final QName key = nextQName();
+
+ skipWhitespaces();
+ checkCurrentAndSkip(EQUALS, "Precondition must contain '='");
+ skipWhitespaces();
+ final Object keyValue = nextCurrentFunctionPathValue();
+ skipWhitespaces();
+ checkCurrentAndSkip(PRECONDITION_END, "Precondition must ends with ']'");
+
+ keyValues.put(key, keyValue);
+ }
+ return new NodeIdentifierWithPredicates(name, keyValues.build());
+ }
+
+ private Object nextCurrentFunctionPathValue() {
+ final String xPathSubStr = xPathString.substring(offset);
+ final String pathKeyExpression = xPathSubStr.substring(0, xPathSubStr.indexOf(PRECONDITION_END));
+ final String relPathKeyExpression = pathKeyExpression.substring(CURRENT_FUNCTION_INVOCATION_STR.length());
+
+ offset += CURRENT_FUNCTION_INVOCATION_STR.length();
+ skipWhitespaces();
+ checkCurrentAndSkip(SLASH, "Expression 'current()' must be followed by slash.");
+ skipWhitespaces();
+
+ final List<String> pathComponents = SLASH_SPLITTER.trimResults().omitEmptyStrings()
+ .splitToList(relPathKeyExpression);
+ checkValid(!pathComponents.isEmpty(), "Malformed path key expression: '%s'.", pathKeyExpression);
+
+ boolean inNodeIdentifierPart = false;
+ NormalizedNodeContext currentNodeCtx = this.currentNodeCtx;
+ NormalizedNode<?, ?> currentNode = null;
+ for (String pathComponent : pathComponents) {
+ final Matcher matcher = NODE_IDENTIFIER_PATTERN.matcher(pathComponent);
+ if (UP_ONE_LEVEL.equals(pathComponent)) {
+ checkValid(!inNodeIdentifierPart, "Up-one-level expression cannot follow concrete path component.");
+ currentNodeCtx = currentNodeCtx.getParent();
+ currentNode = currentNodeCtx.getNode();
+ offset += UP_ONE_LEVEL.length() + 1;
+ } else if (matcher.matches()) {
+ inNodeIdentifierPart = true;
+ if (currentNode != null && currentNode instanceof DataContainerNode) {
+ final DataContainerNode dcn = (DataContainerNode) currentNode;
+ final Optional<NormalizedNode<?, ?>> possibleChild = dcn.getChild(new NodeIdentifier(nextQName()));
+ currentNode = possibleChild.isPresent() ? possibleChild.get() : null;
+ }
+ } else {
+ throw new IllegalArgumentException(String.format(
+ "Could not parse leafref path '%s'. Offset: %s : Reason: Malformed path component: '%s'.",
+ xPathString, offset, pathComponent));
+ }
+ }
+
+ if (currentNode != null && currentNode instanceof LeafNode) {
+ return currentNode.getValue();
+ }
+
+ throw new IllegalArgumentException("Could not resolve current function path value.");
+
+ }
+
+ /**
+ *
+ * Returns following QName and sets offset to end of QName.
+ *
+ * @return following QName.
+ */
+ private QName nextQName() {
+ // Consume prefix or identifier
+ final String maybePrefix = nextIdentifier();
+ final String prefix, localName;
+ if (!allCharactersConsumed() && COLON == currentChar()) {
+ // previous token is prefix;
+ prefix = maybePrefix;
+ skipCurrentChar();
+ localName = nextIdentifier();
+ } else {
+ prefix = "";
+ localName = maybePrefix;
+ }
+ return createQName(prefix, localName);
+ }
+
+ /**
+ * Returns true if all characters from input string
+ * were consumed.
+ *
+ * @return true if all characters from input string
+ * were consumed.
+ */
+ private boolean allCharactersConsumed() {
+ return offset == xPathString.length();
+ }
+
+ private QName createQName(final String prefix, final String localName) {
+ final Module module = schemaContext.findModuleByNamespaceAndRevision(schemaNode.getQName().getNamespace(),
+ schemaNode.getQName().getRevision());
+ if (prefix.isEmpty() || module.getPrefix().equals(prefix)) {
+ return QName.create(module.getQNameModule(), localName);
+ }
+
+ for (final ModuleImport moduleImport : module.getImports()) {
+ if (prefix.equals(moduleImport.getPrefix())) {
+ final Module importedModule = schemaContext.findModuleByName(moduleImport.getModuleName(),
+ moduleImport.getRevision());
+ return QName.create(importedModule.getQNameModule(),localName);
+ }
+ }
+
+ throw new IllegalArgumentException(String.format("Failed to lookup a module for prefix %s", prefix));
+ }
+
+ /**
+ *
+ * Skips current char if it equals expected otherwise fails parsing.
+ *
+ * @param expected Expected character
+ * @param errorMsg Error message if {@link #currentChar()} does not match expected.
+ */
+ private void checkCurrentAndSkip(final char expected, final String errorMsg) {
+ checkValid(expected == currentChar(), errorMsg);
+ offset++;
+ }
+
+ /**
+ *
+ * Fails parsing if condition is not met.
+ *
+ * In case of error provides pointer to failed leafref,
+ * offset on which failure occured with explanation.
+ *
+ * @param condition Fails parsing if {@code condition} is false
+ * @param errorMsg Error message which will be provided to user.
+ * @param attributes
+ */
+ private void checkValid(final boolean condition, final String errorMsg, final Object... attributes) {
+ if (!condition) {
+ throw new IllegalArgumentException(String.format(
+ "Could not parse leafref path '%s'. Offset: %s : Reason: %s", xPathString, offset,
+ String.format(errorMsg, attributes)));
+ }
+ }
+
+ /**
+ * Returns character at current offset.
+ *
+ * @return character at current offset.
+ */
+ private char currentChar() {
+ return xPathString.charAt(offset);
+ }
+
+ /**
+ * Increases processing offset by 1
+ */
+ private void skipCurrentChar() {
+ offset++;
+ }
+
+ /**
+ * Skip whitespace characters, sets offset to first following
+ * non-whitespace character.
+ */
+ private void skipWhitespaces() {
+ nextSequenceEnd(WSP);
+ }
+
+ /**
+ * Returns a string which matches IDENTIFIER YANG ABNF token
+ * and sets processing offset after the end of identifier.
+ *
+ * @return string which matches IDENTIFIER YANG ABNF token
+ */
+ private String nextIdentifier() {
+ int start = offset;
+ checkValid(IDENTIFIER_FIRST_CHAR.matches(currentChar()), "Identifier must start with character from set 'a-zA-Z_'");
+ nextSequenceEnd(IDENTIFIER);
+ return xPathString.substring(start, offset);
+ }
+
+ private void nextSequenceEnd(final CharMatcher matcher) {
+ while (!allCharactersConsumed() && matcher.matches(xPathString.charAt(offset))) {
+ offset++;
+ }
+ }
+}
\ No newline at end of file
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodes;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
final class NormalizedNodeContextSupport extends ContextSupport {
private static final long serialVersionUID = 1L;
return result;
}
+ SchemaContext getSchemaContext() {
+ return getNavigator().getSchemaContext();
+ }
+
@Override
public NormalizedNodeNavigator getNavigator() {
return (NormalizedNodeNavigator) super.getNavigator();
import java.util.Map.Entry;
import java.util.NoSuchElementException;
import java.util.Set;
+import javax.annotation.Nonnull;
import org.jaxen.DefaultNavigator;
import org.jaxen.NamedAccessNavigator;
import org.jaxen.Navigator;
import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
/**
* A {@link Navigator} implementation for YANG XPaths instantiated on a particular root {@link NormalizedNode}.
return document.getRootNode();
}
+ @Nonnull
+ SchemaContext getSchemaContext() {
+ return document.getSchemaContext();
+ }
+
private static final class NormalizedNodeContextIterator extends UnmodifiableIterator<NormalizedNodeContext> {
private NormalizedNodeContext next;
*/
package org.opendaylight.yangtools.yang.data.jaxen;
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Splitter;
import com.google.common.base.Verify;
+import java.util.ArrayDeque;
+import java.util.Deque;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import org.jaxen.ContextSupport;
import org.jaxen.Function;
import org.jaxen.FunctionCallException;
import org.jaxen.FunctionContext;
+import org.jaxen.JaxenRuntimeException;
import org.jaxen.UnresolvableException;
+import org.jaxen.UnsupportedAxisException;
import org.jaxen.XPathFunctionContext;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodes;
+import org.opendaylight.yangtools.yang.model.api.IdentitySchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.ModuleImport;
+import org.opendaylight.yangtools.yang.model.api.RevisionAwareXPath;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.TypedSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.InstanceIdentifierTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition;
+import org.opendaylight.yangtools.yang.model.util.RegexUtils;
+import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
/**
- * A {@link FunctionContext} which contains also to YANG-specific current() function.
+ * A {@link FunctionContext} which contains also YANG-specific functions current(), re-match(), deref(),
+ * derived-from(), derived-from-or-self(), enum-value() and bit-is-set().
*/
final class YangFunctionContext implements FunctionContext {
+ private static final Splitter COLON_SPLITTER = Splitter.on(':');
+ private static final Double DOUBLE_NAN = Double.NaN;
+
// Core XPath functions, as per http://tools.ietf.org/html/rfc6020#section-6.4.1
private static final FunctionContext XPATH_FUNCTION_CONTEXT = new XPathFunctionContext(false);
// current() function, as per http://tools.ietf.org/html/rfc6020#section-6.4.1
return ((NormalizedNodeContext) context);
};
+ // re-match(string subject, string pattern) function as per https://tools.ietf.org/html/rfc7950#section-10.2.1
+ private static final Function REMATCH_FUNCTION = (context, args) -> {
+ if (args == null || args.size() != 2) {
+ throw new FunctionCallException("re-match() takes two arguments: string subject, string pattern.");
+ }
+
+ if (!(args.get(0) instanceof String)) {
+ throw new FunctionCallException("First argument of re-match() should be a String.");
+ }
+
+ if (!(args.get(1) instanceof String)) {
+ throw new FunctionCallException("Second argument of re-match() should be a String.");
+ }
+
+ final String subject = (String) args.get(0);
+ final String rawPattern = (String) args.get(1);
+
+ final String pattern = RegexUtils.getJavaRegexFromXSD(rawPattern);
+
+ return (Boolean) subject.matches(pattern);
+ };
+
+ // deref(node-set nodes) function as per https://tools.ietf.org/html/rfc7950#section-10.3.1
+ private static final Function DEREF_FUNCTION = (context, args) -> {
+ if (!args.isEmpty()) {
+ throw new FunctionCallException("deref() takes only one argument: node-set nodes.");
+ }
+
+ Verify.verify(context instanceof NormalizedNodeContext, "Unhandled context %s", context.getClass());
+
+ final NormalizedNodeContext currentNodeContext = (NormalizedNodeContext) context;
+ final SchemaContext schemaContext = getSchemaContext(currentNodeContext);
+ final TypedSchemaNode correspondingSchemaNode = getCorrespondingTypedSchemaNode(schemaContext, currentNodeContext);
+
+ final Object nodeValue = currentNodeContext.getNode().getValue();
+
+ if (correspondingSchemaNode.getType() instanceof InstanceIdentifierTypeDefinition
+ && nodeValue instanceof YangInstanceIdentifier) {
+ return getNodeReferencedByInstanceIdentifier((YangInstanceIdentifier) nodeValue, currentNodeContext);
+ }
+
+ if (correspondingSchemaNode.getType() instanceof LeafrefTypeDefinition) {
+ final LeafrefTypeDefinition leafrefType = (LeafrefTypeDefinition) correspondingSchemaNode.getType();
+ final RevisionAwareXPath xPath = leafrefType.getPathStatement();
+ if (xPath.isAbsolute()) {
+ final NormalizedNode<?, ?> referencedNode = getNodeReferencedByAbsoluteLeafref(
+ xPath, currentNodeContext, schemaContext, correspondingSchemaNode);
+ if (referencedNode.getValue().equals(nodeValue)) {
+ return referencedNode;
+ }
+ } else {
+ final NormalizedNode<?, ?> referencedNode = getNodeReferencedByRelativeLeafref(
+ xPath, currentNodeContext, schemaContext, correspondingSchemaNode);
+ if (referencedNode.getValue().equals(nodeValue)) {
+ return referencedNode;
+ }
+ }
+ }
+
+ return null;
+ };
+
+ private static NormalizedNode<?, ?> getNodeReferencedByInstanceIdentifier(final YangInstanceIdentifier path,
+ final NormalizedNodeContext currentNodeContext) {
+ final NormalizedNodeNavigator navigator = (NormalizedNodeNavigator) currentNodeContext.getNavigator();
+ final NormalizedNode<?, ?> rootNode = navigator.getRootNode();
+ final List<PathArgument> pathArguments = path.getPathArguments();
+ if (pathArguments.get(0).getNodeType().equals(rootNode.getNodeType())) {
+ final List<PathArgument> relPath = pathArguments.subList(1, pathArguments.size());
+ final Optional<NormalizedNode<?, ?>> possibleNode = NormalizedNodes.findNode(rootNode, relPath);
+ if (possibleNode.isPresent()) {
+ return possibleNode.get();
+ }
+
+ return null;
+ }
+
+ return null;
+ }
+
+ private static NormalizedNode<?, ?> getNodeReferencedByAbsoluteLeafref(final RevisionAwareXPath xPath,
+ final NormalizedNodeContext currentNodeContext, final SchemaContext schemaContext,
+ final TypedSchemaNode correspondingSchemaNode) {
+ final LeafrefXPathStringParsingPathArgumentBuilder builder = new LeafrefXPathStringParsingPathArgumentBuilder(
+ xPath.toString(), schemaContext, correspondingSchemaNode, currentNodeContext);
+ final List<PathArgument> pathArguments = builder.build();
+ final NormalizedNodeNavigator navigator = (NormalizedNodeNavigator) currentNodeContext.getNavigator();
+ final NormalizedNode<?, ?> rootNode = navigator.getRootNode();
+ if (pathArguments.get(0).getNodeType().equals(rootNode.getNodeType())) {
+ final List<PathArgument> relPath = pathArguments.subList(1, pathArguments.size());
+ final Optional<NormalizedNode<?, ?>> possibleNode = NormalizedNodes.findNode(rootNode, relPath);
+ if (possibleNode.isPresent()) {
+ return possibleNode.get();
+ }
+
+ return null;
+ }
+
+ return null;
+ }
+
+ private static NormalizedNode<?, ?> getNodeReferencedByRelativeLeafref(final RevisionAwareXPath xPath,
+ final NormalizedNodeContext currentNodeContext, final SchemaContext schemaContext,
+ final TypedSchemaNode correspondingSchemaNode) {
+ NormalizedNodeContext relativeNodeContext = currentNodeContext;
+ final StringBuilder xPathStringBuilder = new StringBuilder(xPath.toString());
+ // strip the relative path of all ../ at the beginning
+ while (xPathStringBuilder.indexOf("../") == 0) {
+ xPathStringBuilder.delete(0, 3);
+ relativeNodeContext = relativeNodeContext.getParent();
+ }
+
+ // add / to the beginning of the path so that it can be processed the same way as an absolute path
+ xPathStringBuilder.insert(0, '/');
+ final LeafrefXPathStringParsingPathArgumentBuilder builder = new LeafrefXPathStringParsingPathArgumentBuilder(
+ xPathStringBuilder.toString(), schemaContext, correspondingSchemaNode, currentNodeContext);
+ final List<PathArgument> pathArguments = builder.build();
+ final NormalizedNode<?, ?> relativeNode = relativeNodeContext.getNode();
+ final Optional<NormalizedNode<?, ?>> possibleNode = NormalizedNodes.findNode(relativeNode, pathArguments);
+ if (possibleNode.isPresent()) {
+ return possibleNode.get();
+ }
+
+ return null;
+ }
+
+ // derived-from(node-set nodes, string identity) function as per https://tools.ietf.org/html/rfc7950#section-10.4.1
+ private static final Function DERIVED_FROM_FUNCTION = (context, args) -> {
+ if (args == null || args.size() != 1) {
+ throw new FunctionCallException("derived-from() takes two arguments: node-set nodes, string identity.");
+ }
+
+ if (!(args.get(0) instanceof String)) {
+ throw new FunctionCallException("Argument 'identity' of derived-from() function should be a String.");
+ }
+
+ final String identityArg = (String) args.get(0);
+
+ Verify.verify(context instanceof NormalizedNodeContext, "Unhandled context %s", context.getClass());
+
+ final NormalizedNodeContext currentNodeContext = (NormalizedNodeContext) context;
+ final SchemaContext schemaContext = getSchemaContext(currentNodeContext);
+ final TypedSchemaNode correspondingSchemaNode = getCorrespondingTypedSchemaNode(schemaContext, currentNodeContext);
+
+ if (!(correspondingSchemaNode.getType() instanceof IdentityrefTypeDefinition)) {
+ return Boolean.FALSE;
+ }
+
+ if (!(currentNodeContext.getNode().getValue() instanceof QName)) {
+ return Boolean.FALSE;
+ }
+
+ final QName currentNodeValue = (QName) currentNodeContext.getNode().getValue();
+
+ final IdentitySchemaNode identityArgSchemaNode = getIdentitySchemaNodeFromString(identityArg, schemaContext,
+ correspondingSchemaNode);
+ final IdentitySchemaNode currentNodeIdentitySchemaNode = getIdentitySchemaNodeFromQName(currentNodeValue,
+ schemaContext);
+
+ final Set<IdentitySchemaNode> ancestorIdentities = new HashSet<>();
+ collectAncestorIdentities(currentNodeIdentitySchemaNode, ancestorIdentities);
+
+ return Boolean.valueOf(ancestorIdentities.contains(identityArgSchemaNode));
+ };
+
+ // derived-from-or-self(node-set nodes, string identity) function as per https://tools.ietf.org/html/rfc7950#section-10.4.2
+ private static final Function DERIVED_FROM_OR_SELF_FUNCTION = (context, args) -> {
+ if (args == null || args.size() != 1) {
+ throw new FunctionCallException("derived-from-or-self() takes two arguments: node-set nodes, string identity");
+ }
+
+ if (!(args.get(0) instanceof String)) {
+ throw new FunctionCallException("Argument 'identity' of derived-from-or-self() function should be a String.");
+ }
+
+ final String identityArg = (String) args.get(0);
+
+ Verify.verify(context instanceof NormalizedNodeContext, "Unhandled context %s", context.getClass());
+
+ final NormalizedNodeContext currentNodeContext = (NormalizedNodeContext) context;
+ final SchemaContext schemaContext = getSchemaContext(currentNodeContext);
+ final TypedSchemaNode correspondingSchemaNode = getCorrespondingTypedSchemaNode(schemaContext, currentNodeContext);
+
+ if (!(correspondingSchemaNode.getType() instanceof IdentityrefTypeDefinition)) {
+ return Boolean.FALSE;
+ }
+
+ if (!(currentNodeContext.getNode().getValue() instanceof QName)) {
+ return Boolean.FALSE;
+ }
+
+ final QName currentNodeValue = (QName) currentNodeContext.getNode().getValue();
+
+ final IdentitySchemaNode identityArgSchemaNode = getIdentitySchemaNodeFromString(identityArg, schemaContext,
+ correspondingSchemaNode);
+ final IdentitySchemaNode currentNodeIdentitySchemaNode = getIdentitySchemaNodeFromQName(currentNodeValue,
+ schemaContext);
+ if (currentNodeIdentitySchemaNode.equals(identityArgSchemaNode)) {
+ return Boolean.TRUE;
+ }
+
+ final Set<IdentitySchemaNode> ancestorIdentities = new HashSet<>();
+ collectAncestorIdentities(currentNodeIdentitySchemaNode, ancestorIdentities);
+
+ return Boolean.valueOf(ancestorIdentities.contains(identityArgSchemaNode));
+ };
+
+ private static void collectAncestorIdentities(final IdentitySchemaNode identity,
+ final Set<IdentitySchemaNode> ancestorIdentities) {
+ for (final IdentitySchemaNode id : identity.getBaseIdentities()) {
+ collectAncestorIdentities(id, ancestorIdentities);
+ ancestorIdentities.add(id);
+ }
+ }
+
+ private static IdentitySchemaNode getIdentitySchemaNodeFromQName(final QName identityQName,
+ final SchemaContext schemaContext) {
+ final Module module = schemaContext.findModuleByNamespaceAndRevision(identityQName.getNamespace(),
+ identityQName.getRevision());
+ return findIdentitySchemaNodeInModule(module, identityQName);
+ }
+
+ private static IdentitySchemaNode getIdentitySchemaNodeFromString(final String identity,
+ final SchemaContext schemaContext, final TypedSchemaNode correspondingSchemaNode) {
+ final List<String> identityPrefixAndName = COLON_SPLITTER.splitToList(identity);
+ final Module module = schemaContext.findModuleByNamespaceAndRevision(
+ correspondingSchemaNode.getQName().getNamespace(), correspondingSchemaNode.getQName().getRevision());
+ if (identityPrefixAndName.size() == 2) {
+ // prefix of local module
+ if (identityPrefixAndName.get(0).equals(module.getPrefix())) {
+ return findIdentitySchemaNodeInModule(module, QName.create(module.getQNameModule(),
+ identityPrefixAndName.get(1)));
+ }
+
+ // prefix of imported module
+ for (final ModuleImport moduleImport : module.getImports()) {
+ if (identityPrefixAndName.get(0).equals(moduleImport.getPrefix())) {
+ final Module importedModule = schemaContext.findModuleByName(moduleImport.getModuleName(),
+ moduleImport.getRevision());
+ return findIdentitySchemaNodeInModule(importedModule, QName.create(
+ importedModule.getQNameModule(), identityPrefixAndName.get(1)));
+ }
+ }
+
+ throw new IllegalArgumentException("Cannot resolve prefix '%s' from identity '%s'.");
+ }
+
+ if (identityPrefixAndName.size() == 1) { // without prefix
+ return findIdentitySchemaNodeInModule(module, QName.create(module.getQNameModule(),
+ identityPrefixAndName.get(0)));
+ }
+
+ throw new IllegalArgumentException(String.format("Malformed identity argument: %s.", identity));
+ }
+
+ private static IdentitySchemaNode findIdentitySchemaNodeInModule(final Module module, final QName identityQName) {
+ for (final IdentitySchemaNode id : module.getIdentities()) {
+ if (identityQName.equals(id.getQName())) {
+ return id;
+ }
+ }
+
+ throw new IllegalArgumentException(String.format("Identity %s does not have a corresponding" +
+ " identity schema node in the module %s.", identityQName, module));
+ }
+
+
+
+ // enum-value(node-set nodes) function as per https://tools.ietf.org/html/rfc7950#section-10.5.1
+ private static final Function ENUM_VALUE_FUNCTION = (context, args) -> {
+ if (!args.isEmpty()) {
+ throw new FunctionCallException("enum-value() takes one argument: node-set nodes.");
+ }
+
+ Verify.verify(context instanceof NormalizedNodeContext, "Unhandled context %s", context.getClass());
+
+ final NormalizedNodeContext currentNodeContext = (NormalizedNodeContext) context;
+ final SchemaContext schemaContext = getSchemaContext(currentNodeContext);
+ final TypedSchemaNode correspondingSchemaNode = getCorrespondingTypedSchemaNode(schemaContext,
+ currentNodeContext);
+
+ if (!(correspondingSchemaNode.getType() instanceof EnumTypeDefinition)) {
+ return DOUBLE_NAN;
+ }
+
+ if (!(currentNodeContext.getNode().getValue() instanceof String)) {
+ return DOUBLE_NAN;
+ }
+
+ final EnumTypeDefinition enumerationType = (EnumTypeDefinition) correspondingSchemaNode.getType();
+ final String enumName = (String) currentNodeContext.getNode().getValue();
+
+ return getEnumValue(enumerationType, enumName);
+ };
+
+ private static int getEnumValue(final EnumTypeDefinition enumerationType, final String enumName) {
+ for (final EnumTypeDefinition.EnumPair enumPair : enumerationType.getValues()) {
+ if (enumName.equals(enumPair.getName())) {
+ return enumPair.getValue();
+ }
+ }
+
+ throw new IllegalStateException(String.format("Enum %s does not belong to enumeration %s.",
+ enumName, enumerationType));
+ }
+
+ // bit-is-set(node-set nodes, string bit-name) function as per https://tools.ietf.org/html/rfc7950#section-10.6.1
+ private static final Function BIT_IS_SET_FUNCTION = (context, args) -> {
+ if (args == null || args.size() != 1) {
+ throw new FunctionCallException("bit-is-set() takes two arguments: node-set nodes, string bit-name");
+ }
+
+ if (!(args.get(0) instanceof String)) {
+ throw new FunctionCallException("Argument bit-name of bit-is-set() function should be a String");
+ }
+
+ final String bitName = (String) args.get(0);
+
+ Verify.verify(context instanceof NormalizedNodeContext, "Unhandled context %s", context.getClass());
+
+ final NormalizedNodeContext currentNodeContext = (NormalizedNodeContext) context;
+ final SchemaContext schemaContext = getSchemaContext(currentNodeContext);
+ final TypedSchemaNode correspondingSchemaNode = getCorrespondingTypedSchemaNode(schemaContext, currentNodeContext);
+
+ final TypeDefinition<?> nodeType = correspondingSchemaNode.getType();
+ if (!(nodeType instanceof BitsTypeDefinition)) {
+ return Boolean.FALSE;
+ }
+
+ final Object nodeValue = currentNodeContext.getNode().getValue();
+ if (!(nodeValue instanceof Set)) {
+ return Boolean.FALSE;
+ }
+
+ final BitsTypeDefinition bitsType = (BitsTypeDefinition) nodeType;
+ Preconditions.checkState(containsBit(bitsType, bitName), "Bit %s does not belong to bits %s.", bitName,
+ bitsType);
+ return Boolean.valueOf(((Set<?>)nodeValue).contains(bitName));
+ };
+
+ private static boolean containsBit(final BitsTypeDefinition bitsType, final String bitName) {
+ for (BitsTypeDefinition.Bit bit : bitsType.getBits()) {
+ if (bitName.equals(bit.getName())) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private static SchemaContext getSchemaContext(final NormalizedNodeContext normalizedNodeContext) {
+ final ContextSupport contextSupport = normalizedNodeContext.getContextSupport();
+ Verify.verify(contextSupport instanceof NormalizedNodeContextSupport, "Unhandled context support %s",
+ contextSupport.getClass());
+ return ((NormalizedNodeContextSupport) contextSupport).getSchemaContext();
+ }
+
+ private static TypedSchemaNode getCorrespondingTypedSchemaNode(final SchemaContext schemaContext,
+ final NormalizedNodeContext currentNodeContext) {
+ Iterator<NormalizedNodeContext> ancestorOrSelfAxisIterator;
+ try {
+ ancestorOrSelfAxisIterator = currentNodeContext.getContextSupport().getNavigator()
+ .getAncestorOrSelfAxisIterator(currentNodeContext);
+ } catch (UnsupportedAxisException ex) {
+ throw new JaxenRuntimeException(ex);
+ }
+
+ final Deque<QName> schemaPathToCurrentNode = new ArrayDeque<>();
+ while (ancestorOrSelfAxisIterator.hasNext()) {
+ final NormalizedNode<?, ?> nextNode = ancestorOrSelfAxisIterator.next().getNode();
+ if (!(nextNode instanceof MapNode) && !(nextNode instanceof LeafSetNode)
+ && !(nextNode instanceof AugmentationNode)) {
+ schemaPathToCurrentNode.addFirst(nextNode.getNodeType());
+ }
+ }
+
+ final SchemaNode schemaNode = SchemaContextUtil.findNodeInSchemaContext(schemaContext, schemaPathToCurrentNode);
+
+ Preconditions.checkNotNull(schemaNode, "Node %s does not have a corresponding SchemaNode in the SchemaContext.",
+ currentNodeContext.getNode());
+ Preconditions.checkState(schemaNode instanceof TypedSchemaNode, "Node %s must be a leaf or a leaf-list.",
+ currentNodeContext.getNode());
+ return (TypedSchemaNode) schemaNode;
+ }
+
// Singleton instance of reuse
private static final YangFunctionContext INSTANCE = new YangFunctionContext();
}
@Override
- public Function getFunction(final String namespaceURI, final String prefix, final String localName) throws UnresolvableException {
- if (prefix == null && "current".equals(localName)) {
- return CURRENT_FUNCTION;
+ public Function getFunction(final String namespaceURI, final String prefix, final String localName)
+ throws UnresolvableException {
+ if (prefix == null) {
+ switch (localName) {
+ case "bit-is-set":
+ return BIT_IS_SET_FUNCTION;
+ case "current":
+ return CURRENT_FUNCTION;
+ case "deref":
+ return DEREF_FUNCTION;
+ case "derived-from":
+ return DERIVED_FROM_FUNCTION;
+ case "derived-from-or-self":
+ return DERIVED_FROM_OR_SELF_FUNCTION;
+ case "enum-value":
+ return ENUM_VALUE_FUNCTION;
+ case "re-match":
+ return REMATCH_FUNCTION;
+ }
}
+
return XPATH_FUNCTION_CONTEXT.getFunction(namespaceURI, prefix, localName);
}
}
--- /dev/null
+/*
+ * 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.data.jaxen;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+
+import com.google.common.collect.BiMap;
+import com.google.common.collect.HashBiMap;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Maps;
+import java.net.URI;
+import java.util.Map;
+import java.util.Set;
+import org.jaxen.Context;
+import org.jaxen.Function;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.SimpleDateFormatUtil;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.xpath.XPathDocument;
+import org.opendaylight.yangtools.yang.data.api.schema.xpath.XPathSchemaContext;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
+
+public class YangXPathFunctionsTest {
+
+ private static JaxenSchemaContextFactory jaxenSchemaContextFactory;
+
+ @BeforeClass
+ public static void setup() {
+ jaxenSchemaContextFactory = new JaxenSchemaContextFactory();
+ }
+
+ @Test
+ public void testRematchFunction() throws Exception {
+ // re-match() uses regex processing from yang-parser-impl which has been thoroughly tested within
+ // the Bug5410Test unit test class, so here is just a basic test
+ final YangFunctionContext yangFunctionContext = YangFunctionContext.getInstance();
+ final Function rematchFunction = yangFunctionContext.getFunction(null, null, "re-match");
+
+ final Context mockedContext = mock(Context.class);
+
+ boolean rematchResult = (boolean) rematchFunction.call(mockedContext, ImmutableList.of("abbc", "[abc]{1,4}"));
+ assertTrue(rematchResult);
+ rematchResult = (boolean) rematchFunction.call(mockedContext, ImmutableList.of("abbcc", "[abc]{1,4}"));
+ assertFalse(rematchResult);
+ }
+
+ @Test
+ public void testDerefFunctionForInstanceIdentifier() throws Exception {
+ final QNameModule fooModule = QNameModule.create(URI.create("foo-ns"),
+ SimpleDateFormatUtil.getRevisionFormat().parse("2017-04-03"));
+ final QName myContainer = QName.create(fooModule, "my-container");
+ final QName myList = QName.create(fooModule, "my-list");
+ final QName keyLeafA = QName.create(fooModule, "key-leaf-a");
+ final QName keyLeafB = QName.create(fooModule, "key-leaf-b");
+ final QName iidLeaf = QName.create(fooModule, "iid-leaf");
+ final QName referencedLeaf = QName.create(fooModule, "referenced-leaf");
+
+ final Map<QName, Object> keyValues = ImmutableMap.of(keyLeafA, "key-value-a", keyLeafB, "key-value-b");
+ final YangInstanceIdentifier iidPath = YangInstanceIdentifier.of(myContainer).node(myList)
+ .node(new NodeIdentifierWithPredicates(myList, keyValues)).node(referencedLeaf);
+
+ final LeafNode<?> iidLeafNode = Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(iidLeaf))
+ .withValue(iidPath).build();
+ final LeafNode<?> referencedLeafNode = Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(referencedLeaf))
+ .withValue("referenced-leaf-node-value").build();
+
+ final MapNode myListNode = Builders.mapBuilder().withNodeIdentifier(new NodeIdentifier(myList))
+ .withChild(Builders.mapEntryBuilder().withNodeIdentifier(
+ new NodeIdentifierWithPredicates(myList, keyValues))
+ .withChild(iidLeafNode)
+ .withChild(referencedLeafNode).build())
+ .build();
+
+ final ContainerNode myContainerNode = Builders.containerBuilder().withNodeIdentifier(
+ new NodeIdentifier(myContainer)).withChild(myListNode).build();
+
+ final SchemaContext schemaContext = YangParserTestUtils.parseYangSource(
+ "/yang-xpath-functions-test/deref-function-iid/foo.yang");
+ assertNotNull(schemaContext);
+
+ final XPathSchemaContext jaxenSchemaContext = jaxenSchemaContextFactory.createContext(schemaContext);
+ final XPathDocument jaxenDocument = jaxenSchemaContext.createDocument(myContainerNode);
+
+ final BiMap<String, QNameModule> converterBiMap = HashBiMap.create();
+ converterBiMap.put("foo-prefix", fooModule);
+
+ final NormalizedNodeContextSupport normalizedNodeContextSupport = NormalizedNodeContextSupport.create(
+ (JaxenDocument) jaxenDocument, Maps.asConverter(converterBiMap));
+
+ final YangInstanceIdentifier path = YangInstanceIdentifier.of(myList)
+ .node(new NodeIdentifierWithPredicates(myList, keyValues)).node(iidLeaf);
+ final NormalizedNodeContext normalizedNodeContext = normalizedNodeContextSupport.createContext(path);
+
+ final Function derefFunction = normalizedNodeContextSupport.getFunctionContext()
+ .getFunction(null, null, "deref");
+ final Object derefResult = derefFunction.call(normalizedNodeContext, ImmutableList.of());
+ assertNotNull(derefResult);
+ assertTrue(derefResult instanceof NormalizedNode<?, ?>);
+ assertSame(referencedLeafNode, derefResult);
+ }
+
+ @Test
+ public void testDerefFunctionForLeafref() throws Exception {
+ final QNameModule fooModule = QNameModule.create(URI.create("foo-ns"),
+ SimpleDateFormatUtil.getRevisionFormat().parse("2017-04-03"));
+ final QName myContainer = QName.create(fooModule, "my-container");
+ final QName myInnerContainer = QName.create(fooModule, "my-inner-container");
+ final QName myList = QName.create(fooModule, "my-list");
+ final QName keyLeafA = QName.create(fooModule, "key-leaf-a");
+ final QName keyLeafB = QName.create(fooModule, "key-leaf-b");
+ final QName absLeafrefLeaf = QName.create(fooModule, "abs-leafref-leaf");
+ final QName relLeafrefLeaf = QName.create(fooModule, "rel-leafref-leaf");
+ final QName ordinaryLeafA = QName.create(fooModule, "ordinary-leaf-a");
+ final QName ordinaryLeafB = QName.create(fooModule, "ordinary-leaf-b");
+ final QName referencedLeaf = QName.create(fooModule, "referenced-leaf");
+
+ final Map<QName, Object> keyValues = ImmutableMap.of(keyLeafA, "value-a", keyLeafB, "value-b");
+
+ final LeafNode<?> absLeafrefNode = Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(absLeafrefLeaf))
+ .withValue("referenced-leaf-node-value").build();
+ final LeafNode<?> relLeafrefNode = Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(relLeafrefLeaf))
+ .withValue("referenced-leaf-node-value").build();
+ final LeafNode<?> ordinaryLeafANode = Builders.leafBuilder().withNodeIdentifier(
+ new NodeIdentifier(ordinaryLeafA)).withValue("value-a").build();
+ final LeafNode<?> ordinaryLeafBNode = Builders.leafBuilder().withNodeIdentifier(
+ new NodeIdentifier(ordinaryLeafB)).withValue("value-b").build();
+
+ final LeafNode<?> referencedLeafNode = Builders.leafBuilder().withNodeIdentifier(
+ new NodeIdentifier(referencedLeaf)).withValue("referenced-leaf-node-value").build();
+
+ final MapNode myListNode = Builders.mapBuilder().withNodeIdentifier(new NodeIdentifier(myList))
+ .withChild(Builders.mapEntryBuilder().withNodeIdentifier(
+ new NodeIdentifierWithPredicates(myList, keyValues))
+ .withChild(referencedLeafNode).build())
+ .build();
+
+ final ContainerNode myInnerContainerNode = Builders.containerBuilder().withNodeIdentifier(
+ new NodeIdentifier(myInnerContainer))
+ .withChild(absLeafrefNode)
+ .withChild(relLeafrefNode)
+ .withChild(ordinaryLeafANode)
+ .withChild(ordinaryLeafBNode).build();
+
+ final ContainerNode myContainerNode = Builders.containerBuilder().withNodeIdentifier(
+ new NodeIdentifier(myContainer))
+ .withChild(myListNode)
+ .withChild(myInnerContainerNode).build();
+
+ final SchemaContext schemaContext = YangParserTestUtils.parseYangSource(
+ "/yang-xpath-functions-test/deref-function-leafref/foo.yang");
+ assertNotNull(schemaContext);
+
+ final XPathSchemaContext jaxenSchemaContext = jaxenSchemaContextFactory.createContext(schemaContext);
+ final XPathDocument jaxenDocument = jaxenSchemaContext.createDocument(myContainerNode);
+
+ final BiMap<String, QNameModule> converterBiMap = HashBiMap.create();
+ converterBiMap.put("foo-prefix", fooModule);
+
+ final NormalizedNodeContextSupport normalizedNodeContextSupport = NormalizedNodeContextSupport.create(
+ (JaxenDocument) jaxenDocument, Maps.asConverter(converterBiMap));
+
+ final YangInstanceIdentifier absLeafrefPath = YangInstanceIdentifier.of(myInnerContainer).node(absLeafrefLeaf);
+ NormalizedNodeContext normalizedNodeContext = normalizedNodeContextSupport.createContext(absLeafrefPath);
+
+ final Function derefFunction = normalizedNodeContextSupport.getFunctionContext()
+ .getFunction(null, null, "deref");
+ Object derefResult = derefFunction.call(normalizedNodeContext, ImmutableList.of());
+ assertNotNull(derefResult);
+ assertTrue(derefResult instanceof NormalizedNode<?, ?>);
+ assertSame(referencedLeafNode, derefResult);
+
+ final YangInstanceIdentifier relLeafrefPath = YangInstanceIdentifier.of(myInnerContainer).node(relLeafrefLeaf);
+ normalizedNodeContext = normalizedNodeContextSupport.createContext(relLeafrefPath);
+
+ derefResult = derefFunction.call(normalizedNodeContext, ImmutableList.of());
+ assertNotNull(derefResult);
+ assertTrue(derefResult instanceof NormalizedNode<?, ?>);
+ assertSame(referencedLeafNode, derefResult);
+ }
+
+ @Test
+ public void testDerivedFromFunction() throws Exception {
+ // also includes test for derived-from-or-self function
+ final QNameModule barModule = QNameModule.create(URI.create("bar-ns"),
+ SimpleDateFormatUtil.getRevisionFormat().parse("2017-04-03"));
+ final QName myContainer = QName.create(barModule, "my-container");
+ final QName myList = QName.create(barModule, "my-list");
+ final QName keyLeaf = QName.create(barModule, "key-leaf");
+ final QName idrefLeaf = QName.create(barModule, "idref-leaf");
+ final QName idC2Identity = QName.create(barModule, "id-c2");
+
+ final LeafNode<?> idrefLeafNode = Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(idrefLeaf))
+ .withValue(idC2Identity).build();
+
+ final MapNode myListNode = Builders.mapBuilder().withNodeIdentifier(new NodeIdentifier(myList))
+ .withChild(Builders.mapEntryBuilder().withNodeIdentifier(
+ new NodeIdentifierWithPredicates(myList, keyLeaf, "key-value"))
+ .withChild(idrefLeafNode).build()).build();
+
+ final ContainerNode myContainerNode = Builders.containerBuilder().withNodeIdentifier(
+ new NodeIdentifier(myContainer)).withChild(myListNode).build();
+
+ final SchemaContext schemaContext = YangParserTestUtils.parseYangSources(
+ "/yang-xpath-functions-test/derived-from-function");
+ assertNotNull(schemaContext);
+
+ final XPathSchemaContext jaxenSchemaContext = jaxenSchemaContextFactory.createContext(schemaContext);
+ final XPathDocument jaxenDocument = jaxenSchemaContext.createDocument(myContainerNode);
+
+ final BiMap<String, QNameModule> converterBiMap = HashBiMap.create();
+ converterBiMap.put("bar-prefix", barModule);
+
+ final NormalizedNodeContextSupport normalizedNodeContextSupport = NormalizedNodeContextSupport.create(
+ (JaxenDocument) jaxenDocument, Maps.asConverter(converterBiMap));
+
+ final ImmutableMap.Builder<QName, Object> builder = ImmutableMap.builder();
+ final ImmutableMap<QName, Object> keys = builder.put(keyLeaf, "key-value").build();
+
+ final YangInstanceIdentifier path = YangInstanceIdentifier.of(myList)
+ .node(new NodeIdentifierWithPredicates(myList, keys)).node(idrefLeaf);
+ final NormalizedNodeContext normalizedNodeContext = normalizedNodeContextSupport.createContext(path);
+
+ final Function derivedFromFunction = normalizedNodeContextSupport.getFunctionContext()
+ .getFunction(null, null, "derived-from");
+
+ assertTrue(getDerivedFromResult(derivedFromFunction, normalizedNodeContext, "foo-prefix:id-a3"));
+ assertTrue(getDerivedFromResult(derivedFromFunction, normalizedNodeContext, "foo-prefix:id-a4"));
+ assertTrue(getDerivedFromResult(derivedFromFunction, normalizedNodeContext, "foo-prefix:id-b2"));
+ assertTrue(getDerivedFromResult(derivedFromFunction, normalizedNodeContext, "bar-prefix:id-b3"));
+ assertTrue(getDerivedFromResult(derivedFromFunction, normalizedNodeContext, "id-b4"));
+
+ assertFalse(getDerivedFromResult(derivedFromFunction, normalizedNodeContext, "foo-prefix:id-a1"));
+ assertFalse(getDerivedFromResult(derivedFromFunction, normalizedNodeContext, "foo-prefix:id-a2"));
+ assertFalse(getDerivedFromResult(derivedFromFunction, normalizedNodeContext, "foo-prefix:id-b1"));
+ assertFalse(getDerivedFromResult(derivedFromFunction, normalizedNodeContext, "foo-prefix:id-c1"));
+ assertFalse(getDerivedFromResult(derivedFromFunction, normalizedNodeContext, "bar-prefix:id-c2"));
+
+ final Function derivedFromOrSelfFunction = normalizedNodeContextSupport.getFunctionContext()
+ .getFunction(null, null, "derived-from-or-self");
+ assertTrue(getDerivedFromResult(derivedFromOrSelfFunction, normalizedNodeContext, "bar-prefix:id-c2"));
+ }
+
+
+
+ @Test
+ public void testEnumValueFunction() throws Exception {
+ final QNameModule fooModule = QNameModule.create(URI.create("foo-ns"),
+ SimpleDateFormatUtil.getRevisionFormat().parse("2017-04-03"));
+ final QName myContainer = QName.create(fooModule, "my-container");
+ final QName alarm = QName.create(fooModule, "alarm");
+ final QName severity = QName.create(fooModule, "severity");
+ final QName ordinaryLeaf = QName.create(fooModule, "ordinary-leaf");
+
+ final LeafNode<?> ordinaryLeafNode = Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(ordinaryLeaf))
+ .withValue("test-value").build();
+
+ final MapNode alarmListNode = Builders.mapBuilder().withNodeIdentifier(new NodeIdentifier(alarm))
+ .withChild(Builders.mapEntryBuilder().withNodeIdentifier(
+ new NodeIdentifierWithPredicates(alarm, severity, "major"))
+ .withChild(ordinaryLeafNode).build()).build();
+
+ final ContainerNode myContainerNode = Builders.containerBuilder().withNodeIdentifier(
+ new NodeIdentifier(myContainer)).withChild(alarmListNode).build();
+
+ final SchemaContext schemaContext = YangParserTestUtils.parseYangSource(
+ "/yang-xpath-functions-test/enum-value-function/foo.yang");
+ assertNotNull(schemaContext);
+
+ final XPathSchemaContext jaxenSchemaContext = jaxenSchemaContextFactory.createContext(schemaContext);
+ final XPathDocument jaxenDocument = jaxenSchemaContext.createDocument(myContainerNode);
+
+ final BiMap<String, QNameModule> converterBiMap = HashBiMap.create();
+ converterBiMap.put("foo-prefix", fooModule);
+
+ final NormalizedNodeContextSupport normalizedNodeContextSupport = NormalizedNodeContextSupport.create(
+ (JaxenDocument) jaxenDocument, Maps.asConverter(converterBiMap));
+
+ final ImmutableMap.Builder<QName, Object> builder = ImmutableMap.builder();
+ final ImmutableMap<QName, Object> keys = builder.put(severity, "major").build();
+
+ final YangInstanceIdentifier path = YangInstanceIdentifier.of(alarm)
+ .node(new NodeIdentifierWithPredicates(alarm, keys)).node(severity);
+ final NormalizedNodeContext normalizedNodeContext = normalizedNodeContextSupport.createContext(path);
+
+ final Function enumValueFunction = normalizedNodeContextSupport.getFunctionContext()
+ .getFunction(null, null, "enum-value");
+ final int enumValueResult = (int) enumValueFunction.call(normalizedNodeContext, ImmutableList.of());
+ assertEquals(5, enumValueResult);
+ }
+
+ @Test
+ public void testBitIsSetFunction() throws Exception {
+ final QNameModule fooModule = QNameModule.create(URI.create("foo-ns"),
+ SimpleDateFormatUtil.getRevisionFormat().parse("2017-04-03"));
+ final QName myContainer = QName.create(fooModule, "my-container");
+ final QName myList = QName.create(fooModule, "my-list");
+ final QName flags = QName.create(fooModule, "flags");
+ final QName ordinaryLeaf = QName.create(fooModule, "ordinary-leaf");
+
+ final LeafNode<?> ordinaryLeafNode = Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(ordinaryLeaf))
+ .withValue("test-value").build();
+
+ final Set<String> setOfBits = ImmutableSet.of("UP", "PROMISCUOUS");
+
+ final MapNode myListNode = Builders.mapBuilder().withNodeIdentifier(new NodeIdentifier(myList))
+ .withChild(Builders.mapEntryBuilder().withNodeIdentifier(
+ new NodeIdentifierWithPredicates(myList, flags, setOfBits))
+ .withChild(ordinaryLeafNode).build()).build();
+
+ final ContainerNode myContainerNode = Builders.containerBuilder().withNodeIdentifier(
+ new NodeIdentifier(myContainer)).withChild(myListNode).build();
+
+ final SchemaContext schemaContext = YangParserTestUtils.parseYangSource(
+ "/yang-xpath-functions-test/bit-is-set-function/foo.yang");
+ assertNotNull(schemaContext);
+
+ final XPathSchemaContext jaxenSchemaContext = jaxenSchemaContextFactory.createContext(schemaContext);
+ final XPathDocument jaxenDocument = jaxenSchemaContext.createDocument(myContainerNode);
+
+ final BiMap<String, QNameModule> converterBiMap = HashBiMap.create();
+ converterBiMap.put("foo-prefix", fooModule);
+
+ final NormalizedNodeContextSupport normalizedNodeContextSupport = NormalizedNodeContextSupport.create(
+ (JaxenDocument) jaxenDocument, Maps.asConverter(converterBiMap));
+
+ final ImmutableMap.Builder<QName, Object> builder = ImmutableMap.builder();
+ final ImmutableMap<QName, Object> keys = builder.put(flags, setOfBits).build();
+
+ final YangInstanceIdentifier path = YangInstanceIdentifier.of(myList)
+ .node(new NodeIdentifierWithPredicates(myList, keys)).node(flags);
+ final NormalizedNodeContext normalizedNodeContext = normalizedNodeContextSupport.createContext(path);
+
+ final Function bitIsSetFunction = normalizedNodeContextSupport.getFunctionContext()
+ .getFunction(null, null, "bit-is-set");
+ boolean bitIsSetResult = (boolean) bitIsSetFunction.call(normalizedNodeContext, ImmutableList.of("UP"));
+ assertTrue(bitIsSetResult);
+ bitIsSetResult = (boolean) bitIsSetFunction.call(normalizedNodeContext, ImmutableList.of("PROMISCUOUS"));
+ assertTrue(bitIsSetResult);
+ bitIsSetResult = (boolean) bitIsSetFunction.call(normalizedNodeContext, ImmutableList.of("DISABLED"));
+ assertFalse(bitIsSetResult);
+ }
+
+ private static boolean getDerivedFromResult(final Function derivedFromFunction, final NormalizedNodeContext nnCtx,
+ final String identityArg) throws Exception {
+ return (boolean) derivedFromFunction.call(nnCtx, ImmutableList.of(identityArg));
+ }
+}
--- /dev/null
+module foo {
+ namespace foo-ns;
+ prefix foo-prefix;
+ yang-version 1.1;
+
+ revision 2017-04-03;
+
+ container my-container {
+ list my-list {
+ key flags;
+
+ leaf flags {
+ type bits {
+ bit UP;
+ bit PROMISCUOUS;
+ bit DISABLED;
+ }
+ }
+
+ leaf ordinary-leaf {
+ type string;
+ }
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+module foo {
+ namespace foo-ns;
+ prefix foo-prefix;
+ yang-version 1.1;
+
+ revision 2017-04-03;
+
+ container my-container {
+ list my-list {
+ key "key-leaf-a key-leaf-b";
+
+ leaf key-leaf-a {
+ type string;
+ }
+
+ leaf key-leaf-b {
+ type string;
+ }
+
+ leaf iid-leaf {
+ type instance-identifier;
+ }
+
+ leaf referenced-leaf {
+ type string;
+ }
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+module foo {
+ namespace foo-ns;
+ prefix foo-prefix;
+ yang-version 1.1;
+
+ revision 2017-04-03;
+
+ container my-container {
+ list my-list {
+ key "key-leaf-a key-leaf-b";
+
+ leaf key-leaf-a {
+ type string;
+ }
+
+ leaf key-leaf-b {
+ type string;
+ }
+
+ leaf referenced-leaf {
+ type string;
+ }
+ }
+
+ container my-inner-container {
+ leaf abs-leafref-leaf {
+ type leafref {
+ path "/my-container/my-list[key-leaf-a=current()/../ordinary-leaf-a]" +
+ "[key-leaf-b=current()/../ordinary-leaf-b]/referenced-leaf";
+ }
+ }
+
+ leaf rel-leafref-leaf {
+ type leafref {
+ path "../../my-list[key-leaf-a=current()/../ordinary-leaf-a]" +
+ "[key-leaf-b=current()/../ordinary-leaf-b]/referenced-leaf";
+ }
+ }
+
+ leaf ordinary-leaf-a {
+ type string;
+ }
+
+ leaf ordinary-leaf-b {
+ type string;
+ }
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+module bar {
+ namespace bar-ns;
+ prefix bar-prefix;
+ yang-version 1.1;
+
+ import foo {
+ prefix foo-prefix;
+ revision-date 2017-04-03;
+ }
+
+ revision 2017-04-03;
+
+ identity id-b3;
+ identity id-b4;
+
+ identity id-c2 {
+ base foo-prefix:id-b2;
+ base id-b3;
+ base id-b4;
+ }
+
+ container my-container {
+ list my-list {
+ key key-leaf;
+
+ leaf key-leaf {
+ type string;
+ }
+
+ leaf idref-leaf {
+ type identityref {
+ base foo-prefix:id-a3;
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+module foo {
+ namespace foo-ns;
+ prefix foo-prefix;
+ yang-version 1.1;
+
+ revision 2017-04-03;
+
+ identity id-a1;
+ identity id-a2;
+ identity id-a3;
+ identity id-a4;
+
+ identity id-b1 {
+ base id-a1;
+ base id-a2;
+ }
+
+ identity id-b2 {
+ base id-a3;
+ base id-a4;
+ }
+
+ identity id-c1 {
+ base id-b1;
+ base id-b2;
+ }
+
+ container my-container {}
+}
\ No newline at end of file
--- /dev/null
+module foo {
+ namespace foo-ns;
+ prefix foo-prefix;
+ yang-version 1.1;
+
+ revision 2017-04-03;
+
+ container my-container {
+ list alarm {
+ key severity;
+
+ leaf ordinary-leaf {
+ type string;
+ }
+
+ leaf severity {
+ type enumeration {
+ enum cleared {
+ value 1;
+ }
+ enum indeterminate {
+ value 2;
+ }
+ enum minor {
+ value 3;
+ }
+ enum warning {
+ value 4;
+ }
+ enum major {
+ value 5;
+ }
+ enum critical {
+ value 6;
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * 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.model.util;
+
+import com.google.common.collect.ImmutableSet;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Utilities for converting YANG XSD regexes into Java-compatible regexes
+ */
+public final class RegexUtils {
+ private static final Logger LOG = LoggerFactory.getLogger(RegexUtils.class);
+ private static final Pattern BETWEEN_CURLY_BRACES_PATTERN = Pattern.compile("\\{(.+?)\\}");
+ private static final Set<String> JAVA_UNICODE_BLOCKS = ImmutableSet.<String>builder()
+ .add("AegeanNumbers")
+ .add("AlchemicalSymbols")
+ .add("AlphabeticPresentationForms")
+ .add("AncientGreekMusicalNotation")
+ .add("AncientGreekNumbers")
+ .add("AncientSymbols")
+ .add("Arabic")
+ .add("ArabicPresentationForms-A")
+ .add("ArabicPresentationForms-B")
+ .add("ArabicSupplement")
+ .add("Armenian")
+ .add("Arrows")
+ .add("Avestan")
+ .add("Balinese")
+ .add("Bamum")
+ .add("BamumSupplement")
+ .add("BasicLatin")
+ .add("Batak")
+ .add("Bengali")
+ .add("BlockElements")
+ .add("Bopomofo")
+ .add("BopomofoExtended")
+ .add("BoxDrawing")
+ .add("Brahmi")
+ .add("BraillePatterns")
+ .add("Buginese")
+ .add("Buhid")
+ .add("ByzantineMusicalSymbols")
+ .add("Carian")
+ .add("Cham")
+ .add("Cherokee")
+ .add("CJKCompatibility")
+ .add("CJKCompatibilityForms")
+ .add("CJKCompatibilityIdeographs")
+ .add("CJKCompatibilityIdeographsSupplement")
+ .add("CJKRadicalsSupplement")
+ .add("CJKStrokes")
+ .add("CJKSymbolsandPunctuation")
+ .add("CJKUnifiedIdeographs")
+ .add("CJKUnifiedIdeographsExtensionA")
+ .add("CJKUnifiedIdeographsExtensionB")
+ .add("CJKUnifiedIdeographsExtensionC")
+ .add("CJKUnifiedIdeographsExtensionD")
+ .add("CombiningDiacriticalMarks")
+ .add("CombiningDiacriticalMarksSupplement")
+ .add("CombiningHalfMarks")
+ .add("CombiningDiacriticalMarksforSymbols")
+ .add("CommonIndicNumberForms")
+ .add("ControlPictures")
+ .add("Coptic")
+ .add("CountingRodNumerals")
+ .add("Cuneiform")
+ .add("CuneiformNumbersandPunctuation")
+ .add("CurrencySymbols")
+ .add("CypriotSyllabary")
+ .add("Cyrillic")
+ .add("CyrillicExtended-A")
+ .add("CyrillicExtended-B")
+ .add("CyrillicSupplementary")
+ .add("Deseret")
+ .add("Devanagari")
+ .add("DevanagariExtended")
+ .add("Dingbats")
+ .add("DominoTiles")
+ .add("EgyptianHieroglyphs")
+ .add("Emoticons")
+ .add("EnclosedAlphanumericSupplement")
+ .add("EnclosedAlphanumerics")
+ .add("EnclosedCJKLettersandMonths")
+ .add("EnclosedIdeographicSupplement")
+ .add("Ethiopic")
+ .add("EthiopicExtended")
+ .add("EthiopicExtended-A")
+ .add("EthiopicSupplement")
+ .add("GeneralPunctuation")
+ .add("GeometricShapes")
+ .add("Georgian")
+ .add("GeorgianSupplement")
+ .add("Glagolitic")
+ .add("Gothic")
+ .add("GreekandCoptic")
+ .add("GreekExtended")
+ .add("Gujarati")
+ .add("Gurmukhi")
+ .add("HalfwidthandFullwidthForms")
+ .add("HangulCompatibilityJamo")
+ .add("HangulJamo")
+ .add("HangulJamoExtended-A")
+ .add("HangulJamoExtended-B")
+ .add("HangulSyllables")
+ .add("Hanunoo")
+ .add("Hebrew")
+ .add("HighPrivateUseSurrogates")
+ .add("HighSurrogates")
+ .add("Hiragana")
+ .add("IdeographicDescriptionCharacters")
+ .add("ImperialAramaic")
+ .add("InscriptionalPahlavi")
+ .add("InscriptionalParthian")
+ .add("IPAExtensions")
+ .add("Javanese")
+ .add("Kaithi")
+ .add("KanaSupplement")
+ .add("Kanbun")
+ .add("Kangxi Radicals")
+ .add("Kannada")
+ .add("Katakana")
+ .add("KatakanaPhoneticExtensions")
+ .add("KayahLi")
+ .add("Kharoshthi")
+ .add("Khmer")
+ .add("KhmerSymbols")
+ .add("Lao")
+ .add("Latin-1Supplement")
+ .add("LatinExtended-A")
+ .add("LatinExtendedAdditional")
+ .add("LatinExtended-B")
+ .add("LatinExtended-C")
+ .add("LatinExtended-D")
+ .add("Lepcha")
+ .add("LetterlikeSymbols")
+ .add("Limbu")
+ .add("LinearBIdeograms")
+ .add("LinearBSyllabary")
+ .add("Lisu")
+ .add("LowSurrogates")
+ .add("Lycian")
+ .add("Lydian")
+ .add("MahjongTiles")
+ .add("Malayalam")
+ .add("Mandaic")
+ .add("MathematicalAlphanumericSymbols")
+ .add("MathematicalOperators")
+ .add("MeeteiMayek")
+ .add("MiscellaneousMathematicalSymbols-A")
+ .add("MiscellaneousMathematicalSymbols-B")
+ .add("MiscellaneousSymbols")
+ .add("MiscellaneousSymbolsandArrows")
+ .add("MiscellaneousSymbolsAndPictographs")
+ .add("MiscellaneousTechnical")
+ .add("ModifierToneLetters")
+ .add("Mongolian")
+ .add("MusicalSymbols")
+ .add("Myanmar")
+ .add("MyanmarExtended-A")
+ .add("NewTaiLue")
+ .add("NKo")
+ .add("NumberForms")
+ .add("Ogham")
+ .add("OlChiki")
+ .add("OldItalic")
+ .add("OldPersian")
+ .add("OldSouthArabian")
+ .add("OldTurkic")
+ .add("OpticalCharacterRecognition")
+ .add("Oriya")
+ .add("Osmanya")
+ .add("Phags-pa")
+ .add("PhaistosDisc")
+ .add("Phoenician")
+ .add("PhoneticExtensions")
+ .add("PhoneticExtensionsSupplement")
+ .add("PlayingCards")
+ .add("PrivateUseArea")
+ .add("Rejang")
+ .add("RumiNumeralSymbols")
+ .add("Runic")
+ .add("Samaritan")
+ .add("Saurashtra")
+ .add("Shavian")
+ .add("Sinhala")
+ .add("SmallFormVariants")
+ .add("SpacingModifierLetters")
+ .add("Specials")
+ .add("Sundanese")
+ .add("SuperscriptsandSubscripts")
+ .add("SupplementalArrows-A")
+ .add("SupplementalArrows-B")
+ .add("SupplementalMathematicalOperators")
+ .add("SupplementalPunctuation")
+ .add("SupplementaryPrivateUseArea-A")
+ .add("SupplementaryPrivateUseArea-B")
+ .add("SylotiNagri")
+ .add("Syriac")
+ .add("Tagalog")
+ .add("Tagbanwa")
+ .add("Tags")
+ .add("TaiLe")
+ .add("TaiTham")
+ .add("TaiViet")
+ .add("TaiXuanJingSymbols")
+ .add("Tamil")
+ .add("Telugu")
+ .add("Thaana")
+ .add("Thai")
+ .add("Tibetan")
+ .add("Tifinagh")
+ .add("TransportAndMapSymbols")
+ .add("Ugaritic")
+ .add("UnifiedCanadianAboriginalSyllabics")
+ .add("UnifiedCanadianAboriginalSyllabicsExtended")
+ .add("Vai")
+ .add("VariationSelectors")
+ .add("VariationSelectorsSupplement")
+ .add("VedicExtensions")
+ .add("VerticalForms")
+ .add("YiRadicals")
+ .add("YiSyllables")
+ .add("YijingHexagramSymbols").build();
+
+ private static final int UNICODE_SCRIPT_FIX_COUNTER = 30;
+
+ private RegexUtils() {
+ throw new UnsupportedOperationException("Utility class should not be instantiated.");
+ }
+
+ /**
+ * Converts XSD regex to Java-compatible regex
+ *
+ * @param xsdRegex XSD regex pattern as it is defined in a YANG source
+ * @return Java-compatible regex
+ */
+ public static String getJavaRegexFromXSD(final String xsdRegex) {
+ return "^" + fixUnicodeScriptPattern(escapeChars(xsdRegex)) + '$';
+ }
+
+ /*
+ * As both '^' and '$' are special anchor characters in java regular
+ * expressions which are implicitly present in XSD regular expressions,
+ * we need to escape them in case they are not defined as part of
+ * character ranges i.e. inside regular square brackets.
+ */
+ private static String escapeChars(final String regex) {
+ final StringBuilder result = new StringBuilder(regex.length());
+ int bracket = 0;
+ boolean escape = false;
+ for (int i = 0; i < regex.length(); i++) {
+ final char ch = regex.charAt(i);
+ switch (ch) {
+ case '[':
+ if (!escape) {
+ bracket++;
+ }
+ escape = false;
+ result.append(ch);
+ break;
+ case ']':
+ if (!escape) {
+ bracket--;
+ }
+ escape = false;
+ result.append(ch);
+ break;
+ case '\\':
+ escape = !escape;
+ result.append(ch);
+ break;
+ case '^':
+ case '$':
+ if (bracket == 0) {
+ result.append('\\');
+ }
+ escape = false;
+ result.append(ch);
+ break;
+ default:
+ escape = false;
+ result.append(ch);
+ }
+ }
+ return result.toString();
+ }
+
+ private static String fixUnicodeScriptPattern(String rawPattern) {
+ for (int i = 0; i < UNICODE_SCRIPT_FIX_COUNTER; i++) {
+ try {
+ Pattern.compile(rawPattern);
+ return rawPattern;
+ } catch(final PatternSyntaxException ex) {
+ LOG.debug("Invalid regex pattern syntax in: {}", rawPattern, ex);
+ if (ex.getMessage().contains("Unknown character script name")) {
+ rawPattern = fixUnknownScripts(ex.getMessage(), rawPattern);
+ } else {
+ return rawPattern;
+ }
+ }
+ }
+
+ LOG.warn("Regex pattern could not be fixed: {}", rawPattern);
+ return rawPattern;
+ }
+
+ private static String fixUnknownScripts(final String exMessage, final String rawPattern) {
+ StringBuilder result = new StringBuilder(rawPattern);
+ final Matcher matcher = BETWEEN_CURLY_BRACES_PATTERN.matcher(exMessage);
+ if (matcher.find()) {
+ final String capturedGroup = matcher.group(1);
+ if (JAVA_UNICODE_BLOCKS.contains(capturedGroup)) {
+ final int idx = rawPattern.indexOf("Is" + capturedGroup);
+ result = result.replace(idx, idx + 2, "In");
+ }
+ }
+ return result.toString();
+ }
+}
\ No newline at end of file
import org.opendaylight.yangtools.yang.model.api.stmt.PatternStatement;
import org.opendaylight.yangtools.yang.model.api.stmt.ReferenceStatement;
import org.opendaylight.yangtools.yang.model.api.type.PatternConstraint;
+import org.opendaylight.yangtools.yang.model.util.RegexUtils;
import org.opendaylight.yangtools.yang.parser.spi.SubstatementValidator;
import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractDeclaredStatement;
import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractStatementSupport;
@Override
public PatternConstraint parseArgumentValue(final StmtContext<?, ?, ?> ctx, final String value) {
- final String pattern = getJavaRegexFromXSD(value);
+ final String pattern = RegexUtils.getJavaRegexFromXSD(value);
try {
Pattern.compile(pattern);
return new PatternConstraintEffectiveImpl(pattern, value, Optional.absent(), Optional.absent());
}
- static String getJavaRegexFromXSD(final String xsdRegex) {
- return "^" + Utils.fixUnicodeScriptPattern(escapeChars(xsdRegex)) + '$';
- }
-
- /*
- * As both '^' and '$' are special anchor characters in java regular
- * expressions which are implicitly present in XSD regular expressions,
- * we need to escape them in case they are not defined as part of
- * character ranges i.e. inside regular square brackets.
- */
- private static String escapeChars(final String regex) {
- final StringBuilder result = new StringBuilder(regex.length());
- int bracket = 0;
- boolean escape = false;
- for (int i = 0; i < regex.length(); i++) {
- final char ch = regex.charAt(i);
- switch (ch) {
- case '[':
- if (!escape) {
- bracket++;
- }
- escape = false;
- result.append(ch);
- break;
- case ']':
- if (!escape) {
- bracket--;
- }
- escape = false;
- result.append(ch);
- break;
- case '\\':
- escape = !escape;
- result.append(ch);
- break;
- case '^':
- case '$':
- if (bracket == 0) {
- result.append('\\');
- }
- escape = false;
- result.append(ch);
- break;
- default:
- escape = false;
- result.append(ch);
- }
- }
- return result.toString();
- }
-
@Override
public PatternStatement createDeclared(final StmtContext<PatternConstraint, PatternStatement, ?> ctx) {
return new PatternStatementImpl(ctx);
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
-import java.util.regex.PatternSyntaxException;
import javax.annotation.Nullable;
import javax.annotation.RegEx;
import javax.xml.xpath.XPath;
import org.slf4j.LoggerFactory;
public final class Utils {
- private static final int UNICODE_SCRIPT_FIX_COUNTER = 30;
private static final Logger LOG = LoggerFactory.getLogger(Utils.class);
private static final CharMatcher LEFT_PARENTHESIS_MATCHER = CharMatcher.is('(');
private static final CharMatcher RIGHT_PARENTHESIS_MATCHER = CharMatcher.is(')');
private static final String YANG_XPATH_FUNCTIONS_STRING =
"(re-match|deref|derived-from(-or-self)?|enum-value|bit-is-set)(\\()";
private static final Pattern YANG_XPATH_FUNCTIONS_PATTERN = Pattern.compile(YANG_XPATH_FUNCTIONS_STRING);
- private static final Pattern BETWEEN_CURLY_BRACES_PATTERN = Pattern.compile("\\{(.+?)\\}");
- private static final Set<String> JAVA_UNICODE_BLOCKS = ImmutableSet.<String>builder()
- .add("AegeanNumbers")
- .add("AlchemicalSymbols")
- .add("AlphabeticPresentationForms")
- .add("AncientGreekMusicalNotation")
- .add("AncientGreekNumbers")
- .add("AncientSymbols")
- .add("Arabic")
- .add("ArabicPresentationForms-A")
- .add("ArabicPresentationForms-B")
- .add("ArabicSupplement")
- .add("Armenian")
- .add("Arrows")
- .add("Avestan")
- .add("Balinese")
- .add("Bamum")
- .add("BamumSupplement")
- .add("BasicLatin")
- .add("Batak")
- .add("Bengali")
- .add("BlockElements")
- .add("Bopomofo")
- .add("BopomofoExtended")
- .add("BoxDrawing")
- .add("Brahmi")
- .add("BraillePatterns")
- .add("Buginese")
- .add("Buhid")
- .add("ByzantineMusicalSymbols")
- .add("Carian")
- .add("Cham")
- .add("Cherokee")
- .add("CJKCompatibility")
- .add("CJKCompatibilityForms")
- .add("CJKCompatibilityIdeographs")
- .add("CJKCompatibilityIdeographsSupplement")
- .add("CJKRadicalsSupplement")
- .add("CJKStrokes")
- .add("CJKSymbolsandPunctuation")
- .add("CJKUnifiedIdeographs")
- .add("CJKUnifiedIdeographsExtensionA")
- .add("CJKUnifiedIdeographsExtensionB")
- .add("CJKUnifiedIdeographsExtensionC")
- .add("CJKUnifiedIdeographsExtensionD")
- .add("CombiningDiacriticalMarks")
- .add("CombiningDiacriticalMarksSupplement")
- .add("CombiningHalfMarks")
- .add("CombiningDiacriticalMarksforSymbols")
- .add("CommonIndicNumberForms")
- .add("ControlPictures")
- .add("Coptic")
- .add("CountingRodNumerals")
- .add("Cuneiform")
- .add("CuneiformNumbersandPunctuation")
- .add("CurrencySymbols")
- .add("CypriotSyllabary")
- .add("Cyrillic")
- .add("CyrillicExtended-A")
- .add("CyrillicExtended-B")
- .add("CyrillicSupplementary")
- .add("Deseret")
- .add("Devanagari")
- .add("DevanagariExtended")
- .add("Dingbats")
- .add("DominoTiles")
- .add("EgyptianHieroglyphs")
- .add("Emoticons")
- .add("EnclosedAlphanumericSupplement")
- .add("EnclosedAlphanumerics")
- .add("EnclosedCJKLettersandMonths")
- .add("EnclosedIdeographicSupplement")
- .add("Ethiopic")
- .add("EthiopicExtended")
- .add("EthiopicExtended-A")
- .add("EthiopicSupplement")
- .add("GeneralPunctuation")
- .add("GeometricShapes")
- .add("Georgian")
- .add("GeorgianSupplement")
- .add("Glagolitic")
- .add("Gothic")
- .add("GreekandCoptic")
- .add("GreekExtended")
- .add("Gujarati")
- .add("Gurmukhi")
- .add("HalfwidthandFullwidthForms")
- .add("HangulCompatibilityJamo")
- .add("HangulJamo")
- .add("HangulJamoExtended-A")
- .add("HangulJamoExtended-B")
- .add("HangulSyllables")
- .add("Hanunoo")
- .add("Hebrew")
- .add("HighPrivateUseSurrogates")
- .add("HighSurrogates")
- .add("Hiragana")
- .add("IdeographicDescriptionCharacters")
- .add("ImperialAramaic")
- .add("InscriptionalPahlavi")
- .add("InscriptionalParthian")
- .add("IPAExtensions")
- .add("Javanese")
- .add("Kaithi")
- .add("KanaSupplement")
- .add("Kanbun")
- .add("Kangxi Radicals")
- .add("Kannada")
- .add("Katakana")
- .add("KatakanaPhoneticExtensions")
- .add("KayahLi")
- .add("Kharoshthi")
- .add("Khmer")
- .add("KhmerSymbols")
- .add("Lao")
- .add("Latin-1Supplement")
- .add("LatinExtended-A")
- .add("LatinExtendedAdditional")
- .add("LatinExtended-B")
- .add("LatinExtended-C")
- .add("LatinExtended-D")
- .add("Lepcha")
- .add("LetterlikeSymbols")
- .add("Limbu")
- .add("LinearBIdeograms")
- .add("LinearBSyllabary")
- .add("Lisu")
- .add("LowSurrogates")
- .add("Lycian")
- .add("Lydian")
- .add("MahjongTiles")
- .add("Malayalam")
- .add("Mandaic")
- .add("MathematicalAlphanumericSymbols")
- .add("MathematicalOperators")
- .add("MeeteiMayek")
- .add("MiscellaneousMathematicalSymbols-A")
- .add("MiscellaneousMathematicalSymbols-B")
- .add("MiscellaneousSymbols")
- .add("MiscellaneousSymbolsandArrows")
- .add("MiscellaneousSymbolsAndPictographs")
- .add("MiscellaneousTechnical")
- .add("ModifierToneLetters")
- .add("Mongolian")
- .add("MusicalSymbols")
- .add("Myanmar")
- .add("MyanmarExtended-A")
- .add("NewTaiLue")
- .add("NKo")
- .add("NumberForms")
- .add("Ogham")
- .add("OlChiki")
- .add("OldItalic")
- .add("OldPersian")
- .add("OldSouthArabian")
- .add("OldTurkic")
- .add("OpticalCharacterRecognition")
- .add("Oriya")
- .add("Osmanya")
- .add("Phags-pa")
- .add("PhaistosDisc")
- .add("Phoenician")
- .add("PhoneticExtensions")
- .add("PhoneticExtensionsSupplement")
- .add("PlayingCards")
- .add("PrivateUseArea")
- .add("Rejang")
- .add("RumiNumeralSymbols")
- .add("Runic")
- .add("Samaritan")
- .add("Saurashtra")
- .add("Shavian")
- .add("Sinhala")
- .add("SmallFormVariants")
- .add("SpacingModifierLetters")
- .add("Specials")
- .add("Sundanese")
- .add("SuperscriptsandSubscripts")
- .add("SupplementalArrows-A")
- .add("SupplementalArrows-B")
- .add("SupplementalMathematicalOperators")
- .add("SupplementalPunctuation")
- .add("SupplementaryPrivateUseArea-A")
- .add("SupplementaryPrivateUseArea-B")
- .add("SylotiNagri")
- .add("Syriac")
- .add("Tagalog")
- .add("Tagbanwa")
- .add("Tags")
- .add("TaiLe")
- .add("TaiTham")
- .add("TaiViet")
- .add("TaiXuanJingSymbols")
- .add("Tamil")
- .add("Telugu")
- .add("Thaana")
- .add("Thai")
- .add("Tibetan")
- .add("Tifinagh")
- .add("TransportAndMapSymbols")
- .add("Ugaritic")
- .add("UnifiedCanadianAboriginalSyllabics")
- .add("UnifiedCanadianAboriginalSyllabicsExtended")
- .add("Vai")
- .add("VariationSelectors")
- .add("VariationSelectorsSupplement")
- .add("VedicExtensions")
- .add("VerticalForms")
- .add("YiRadicals")
- .add("YiSyllables")
- .add("YijingHexagramSymbols").build();
private static final Map<String, DeviateKind> KEYWORD_TO_DEVIATE_MAP;
static {
return string;
}
- public static String fixUnicodeScriptPattern(String rawPattern) {
- for (int i = 0; i < UNICODE_SCRIPT_FIX_COUNTER; i++) {
- try {
- Pattern.compile(rawPattern);
- return rawPattern;
- } catch(final PatternSyntaxException ex) {
- LOG.debug("Invalid regex pattern syntax in: {}", rawPattern, ex);
- if (ex.getMessage().contains("Unknown character script name")) {
- rawPattern = fixUnknownScripts(ex.getMessage(), rawPattern);
- } else {
- return rawPattern;
- }
- }
- }
-
- LOG.warn("Regex pattern could not be fixed: {}", rawPattern);
- return rawPattern;
- }
-
- private static String fixUnknownScripts(final String exMessage, final String rawPattern) {
- StringBuilder result = new StringBuilder(rawPattern);
- final Matcher matcher = BETWEEN_CURLY_BRACES_PATTERN.matcher(exMessage);
- if (matcher.find()) {
- final String capturedGroup = matcher.group(1);
- if (JAVA_UNICODE_BLOCKS.contains(capturedGroup)) {
- final int idx = rawPattern.indexOf("Is" + capturedGroup);
- result = result.replace(idx, idx + 2, "In");
- }
- }
- return result.toString();
- }
-
public static boolean belongsToTheSameModule(final QName targetStmtQName, final QName sourceStmtQName) {
if (targetStmtQName.getModule().equals(sourceStmtQName.getModule())) {
return true;
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 String javaRegexFromXSD(final String xsdRegex) {
- return PatternStatementImpl.Definition.getJavaRegexFromXSD(xsdRegex);
+ return RegexUtils.getJavaRegexFromXSD(xsdRegex);
}
private static boolean testMatch(final String javaRegex, final String value) {
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import org.junit.Test;
-import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.Utils;
+import org.opendaylight.yangtools.yang.model.util.RegexUtils;
public class Bug4079Test {
@Test
public void testValidPatternFix() {
- String fixedUnicodeScriptPattern = Utils.fixUnicodeScriptPattern("(\\p{IsArrows})*+");
- assertEquals("(\\p{InArrows})*+", fixedUnicodeScriptPattern);
+ String fixedUnicodeScriptPattern = RegexUtils.getJavaRegexFromXSD("(\\p{IsArrows})*+");
+ assertEquals("^(\\p{InArrows})*+$", fixedUnicodeScriptPattern);
assertNotNull(Pattern.compile(fixedUnicodeScriptPattern));
- fixedUnicodeScriptPattern = Utils.fixUnicodeScriptPattern("(\\p{IsDingbats})++");
- assertEquals("(\\p{InDingbats})++", fixedUnicodeScriptPattern);
+ fixedUnicodeScriptPattern = RegexUtils.getJavaRegexFromXSD("(\\p{IsDingbats})++");
+ assertEquals("^(\\p{InDingbats})++$", fixedUnicodeScriptPattern);
assertNotNull(Pattern.compile(fixedUnicodeScriptPattern));
- fixedUnicodeScriptPattern = Utils.fixUnicodeScriptPattern("(\\p{IsSpecials})?+");
- assertEquals("(\\p{InSpecials})?+", fixedUnicodeScriptPattern);
+ fixedUnicodeScriptPattern = RegexUtils.getJavaRegexFromXSD("(\\p{IsSpecials})?+");
+ assertEquals("^(\\p{InSpecials})?+$", fixedUnicodeScriptPattern);
assertNotNull(Pattern.compile(fixedUnicodeScriptPattern));
- fixedUnicodeScriptPattern = Utils.fixUnicodeScriptPattern("(\\p{IsBatak}){4}+");
- assertEquals("(\\p{IsBatak}){4}+", fixedUnicodeScriptPattern);
+ fixedUnicodeScriptPattern = RegexUtils.getJavaRegexFromXSD("(\\p{IsBatak}){4}+");
+ assertEquals("^(\\p{IsBatak}){4}+$", fixedUnicodeScriptPattern);
assertNotNull(Pattern.compile(fixedUnicodeScriptPattern));
- fixedUnicodeScriptPattern = Utils.fixUnicodeScriptPattern("(\\p{IsLatin}){4,6}+");
- assertEquals("(\\p{IsLatin}){4,6}+", fixedUnicodeScriptPattern);
+ fixedUnicodeScriptPattern = RegexUtils.getJavaRegexFromXSD("(\\p{IsLatin}){4,6}+");
+ assertEquals("^(\\p{IsLatin}){4,6}+$", fixedUnicodeScriptPattern);
assertNotNull(Pattern.compile(fixedUnicodeScriptPattern));
- fixedUnicodeScriptPattern = Utils.fixUnicodeScriptPattern("(\\p{IsTibetan}){4,}+");
- assertEquals("(\\p{IsTibetan}){4,}+", fixedUnicodeScriptPattern);
+ fixedUnicodeScriptPattern = RegexUtils.getJavaRegexFromXSD("(\\p{IsTibetan}){4,}+");
+ assertEquals("^(\\p{IsTibetan}){4,}+$", fixedUnicodeScriptPattern);
assertNotNull(Pattern.compile(fixedUnicodeScriptPattern));
- fixedUnicodeScriptPattern = Utils.fixUnicodeScriptPattern("(\\p{IsAlphabetic}){4}?");
- assertEquals("(\\p{IsAlphabetic}){4}?", fixedUnicodeScriptPattern);
+ fixedUnicodeScriptPattern = RegexUtils.getJavaRegexFromXSD("(\\p{IsAlphabetic}){4}?");
+ assertEquals("^(\\p{IsAlphabetic}){4}?$", fixedUnicodeScriptPattern);
assertNotNull(Pattern.compile(fixedUnicodeScriptPattern));
- fixedUnicodeScriptPattern = Utils.fixUnicodeScriptPattern("(\\p{IsLowercase}){4,6}?");
- assertEquals("(\\p{IsLowercase}){4,6}?", fixedUnicodeScriptPattern);
+ fixedUnicodeScriptPattern = RegexUtils.getJavaRegexFromXSD("(\\p{IsLowercase}){4,6}?");
+ assertEquals("^(\\p{IsLowercase}){4,6}?$", fixedUnicodeScriptPattern);
assertNotNull(Pattern.compile(fixedUnicodeScriptPattern));
- fixedUnicodeScriptPattern = Utils.fixUnicodeScriptPattern("(\\p{IsUppercase}){4,}?");
- assertEquals("(\\p{IsUppercase}){4,}?", fixedUnicodeScriptPattern);
+ fixedUnicodeScriptPattern = RegexUtils.getJavaRegexFromXSD("(\\p{IsUppercase}){4,}?");
+ assertEquals("^(\\p{IsUppercase}){4,}?$", fixedUnicodeScriptPattern);
assertNotNull(Pattern.compile(fixedUnicodeScriptPattern));
- fixedUnicodeScriptPattern = Utils.fixUnicodeScriptPattern("(\\p{IsBasicLatin}|\\p{IsLatin-1Supplement})*");
- assertEquals("(\\p{InBasicLatin}|\\p{InLatin-1Supplement})*", fixedUnicodeScriptPattern);
+ fixedUnicodeScriptPattern = RegexUtils.getJavaRegexFromXSD("(\\p{IsBasicLatin}|\\p{IsLatin-1Supplement})*");
+ assertEquals("^(\\p{InBasicLatin}|\\p{InLatin-1Supplement})*$", fixedUnicodeScriptPattern);
assertNotNull(Pattern.compile(fixedUnicodeScriptPattern));
- fixedUnicodeScriptPattern = Utils.fixUnicodeScriptPattern("(\\p{InBasicLatin}|\\p{InLatin-1Supplement})+");
- assertEquals("(\\p{InBasicLatin}|\\p{InLatin-1Supplement})+", fixedUnicodeScriptPattern);
+ fixedUnicodeScriptPattern = RegexUtils.getJavaRegexFromXSD("(\\p{InBasicLatin}|\\p{InLatin-1Supplement})+");
+ assertEquals("^(\\p{InBasicLatin}|\\p{InLatin-1Supplement})+$", fixedUnicodeScriptPattern);
assertNotNull(Pattern.compile(fixedUnicodeScriptPattern));
- fixedUnicodeScriptPattern = Utils.fixUnicodeScriptPattern("(\\p{IsBasicLatin}|\\p{InLatin-1Supplement})?");
- assertEquals("(\\p{InBasicLatin}|\\p{InLatin-1Supplement})?", fixedUnicodeScriptPattern);
+ fixedUnicodeScriptPattern = RegexUtils.getJavaRegexFromXSD("(\\p{IsBasicLatin}|\\p{InLatin-1Supplement})?");
+ assertEquals("^(\\p{InBasicLatin}|\\p{InLatin-1Supplement})?$", fixedUnicodeScriptPattern);
assertNotNull(Pattern.compile(fixedUnicodeScriptPattern));
- fixedUnicodeScriptPattern = Utils.fixUnicodeScriptPattern("(\\p{InBasicLatin}|\\p{IsLatin-1Supplement}){4}");
- assertEquals("(\\p{InBasicLatin}|\\p{InLatin-1Supplement}){4}", fixedUnicodeScriptPattern);
+ fixedUnicodeScriptPattern = RegexUtils.getJavaRegexFromXSD("(\\p{InBasicLatin}|\\p{IsLatin-1Supplement}){4}");
+ assertEquals("^(\\p{InBasicLatin}|\\p{InLatin-1Supplement}){4}$", fixedUnicodeScriptPattern);
assertNotNull(Pattern.compile(fixedUnicodeScriptPattern));
- fixedUnicodeScriptPattern = Utils.fixUnicodeScriptPattern("(\\p{IsLatin}|\\p{IsArmenian}){2,4}");
- assertEquals("(\\p{IsLatin}|\\p{IsArmenian}){2,4}", fixedUnicodeScriptPattern);
+ fixedUnicodeScriptPattern = RegexUtils.getJavaRegexFromXSD("(\\p{IsLatin}|\\p{IsArmenian}){2,4}");
+ assertEquals("^(\\p{IsLatin}|\\p{IsArmenian}){2,4}$", fixedUnicodeScriptPattern);
assertNotNull(Pattern.compile(fixedUnicodeScriptPattern));
- fixedUnicodeScriptPattern = Utils.fixUnicodeScriptPattern("(\\p{IsLatin}|\\p{IsBasicLatin}){2,}");
- assertEquals("(\\p{IsLatin}|\\p{InBasicLatin}){2,}", fixedUnicodeScriptPattern);
+ fixedUnicodeScriptPattern = RegexUtils.getJavaRegexFromXSD("(\\p{IsLatin}|\\p{IsBasicLatin}){2,}");
+ assertEquals("^(\\p{IsLatin}|\\p{InBasicLatin}){2,}$", fixedUnicodeScriptPattern);
assertNotNull(Pattern.compile(fixedUnicodeScriptPattern));
- fixedUnicodeScriptPattern = Utils.fixUnicodeScriptPattern("(\\p{IsBasicLatin}|\\p{IsLatin})*?");
- assertEquals("(\\p{InBasicLatin}|\\p{IsLatin})*?", fixedUnicodeScriptPattern);
+ fixedUnicodeScriptPattern = RegexUtils.getJavaRegexFromXSD("(\\p{IsBasicLatin}|\\p{IsLatin})*?");
+ assertEquals("^(\\p{InBasicLatin}|\\p{IsLatin})*?$", fixedUnicodeScriptPattern);
assertNotNull(Pattern.compile(fixedUnicodeScriptPattern));
- fixedUnicodeScriptPattern = Utils.fixUnicodeScriptPattern(
+ fixedUnicodeScriptPattern = RegexUtils.getJavaRegexFromXSD(
"(\\p{IsBasicLatin}|\\p{IsLatin-1Supplement}|\\p{IsArrows})+?");
- assertEquals("(\\p{InBasicLatin}|\\p{InLatin-1Supplement}|\\p{InArrows})+?", fixedUnicodeScriptPattern);
+ assertEquals("^(\\p{InBasicLatin}|\\p{InLatin-1Supplement}|\\p{InArrows})+?$", fixedUnicodeScriptPattern);
assertNotNull(Pattern.compile(fixedUnicodeScriptPattern));
- fixedUnicodeScriptPattern = Utils.fixUnicodeScriptPattern(
+ fixedUnicodeScriptPattern = RegexUtils.getJavaRegexFromXSD(
"(\\p{InBasicLatin}|\\p{IsLatin-1Supplement}|\\p{IsLatin})??");
- assertEquals("(\\p{InBasicLatin}|\\p{InLatin-1Supplement}|\\p{IsLatin})??", fixedUnicodeScriptPattern);
+ assertEquals("^(\\p{InBasicLatin}|\\p{InLatin-1Supplement}|\\p{IsLatin})??$", fixedUnicodeScriptPattern);
assertNotNull(Pattern.compile(fixedUnicodeScriptPattern));
- fixedUnicodeScriptPattern = Utils.fixUnicodeScriptPattern("(\\\\\\p{IsBasicLatin})*+");
- assertEquals("(\\\\\\p{InBasicLatin})*+", fixedUnicodeScriptPattern);
+ fixedUnicodeScriptPattern = RegexUtils.getJavaRegexFromXSD("(\\\\\\p{IsBasicLatin})*+");
+ assertEquals("^(\\\\\\p{InBasicLatin})*+$", fixedUnicodeScriptPattern);
assertNotNull(Pattern.compile(fixedUnicodeScriptPattern));
- fixedUnicodeScriptPattern = Utils.fixUnicodeScriptPattern("(\\\\\\\\\\p{IsBasicLatin})*+");
- assertEquals("(\\\\\\\\\\p{InBasicLatin})*+", fixedUnicodeScriptPattern);
+ fixedUnicodeScriptPattern = RegexUtils.getJavaRegexFromXSD("(\\\\\\\\\\p{IsBasicLatin})*+");
+ assertEquals("^(\\\\\\\\\\p{InBasicLatin})*+$", fixedUnicodeScriptPattern);
assertNotNull(Pattern.compile(fixedUnicodeScriptPattern));
- fixedUnicodeScriptPattern = Utils.fixUnicodeScriptPattern("(\\\\\\\\\\\\\\p{IsBasicLatin})*+");
- assertEquals("(\\\\\\\\\\\\\\p{InBasicLatin})*+", fixedUnicodeScriptPattern);
+ fixedUnicodeScriptPattern = RegexUtils.getJavaRegexFromXSD("(\\\\\\\\\\\\\\p{IsBasicLatin})*+");
+ assertEquals("^(\\\\\\\\\\\\\\p{InBasicLatin})*+$", fixedUnicodeScriptPattern);
assertNotNull(Pattern.compile(fixedUnicodeScriptPattern));
}
@Test(expected = PatternSyntaxException.class)
public void testInvalidPattern() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
- String fixedUnicodeScriptPattern = Utils.fixUnicodeScriptPattern("(\\\\p{IsBasicLatin})*+");
- assertEquals("(\\\\p{IsBasicLatin})*+", fixedUnicodeScriptPattern);
+ String fixedUnicodeScriptPattern = RegexUtils.getJavaRegexFromXSD("(\\\\p{IsBasicLatin})*+");
+ assertEquals("^(\\\\p{IsBasicLatin})*+$", fixedUnicodeScriptPattern);
// should throw exception
Pattern.compile(fixedUnicodeScriptPattern);
}
@Test(expected = PatternSyntaxException.class)
public void testInvalidPattern2() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
- String fixedUnicodeScriptPattern = Utils.fixUnicodeScriptPattern("(\\p{IsSpecials}|\\\\\\\\p{IsBasicLatin})*+");
- assertEquals("(\\p{InSpecials}|\\\\\\\\p{IsBasicLatin})*+", fixedUnicodeScriptPattern);
+ String fixedUnicodeScriptPattern = RegexUtils.getJavaRegexFromXSD("(\\p{IsSpecials}|\\\\\\\\p{IsBasicLatin})*+");
+ assertEquals("^(\\p{InSpecials}|\\\\\\\\p{IsBasicLatin})*+$", fixedUnicodeScriptPattern);
// should throw exception
Pattern.compile(fixedUnicodeScriptPattern);
}
@Test(expected = PatternSyntaxException.class)
public void testInvalidPattern3() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
- String fixedUnicodeScriptPattern = Utils.fixUnicodeScriptPattern("(\\\\\\\\\\\\p{IsBasicLatin}|\\p{IsTags})*+");
- assertEquals("(\\\\\\\\\\\\p{IsBasicLatin}|\\p{IsTags})*+", fixedUnicodeScriptPattern);
+ String fixedUnicodeScriptPattern = RegexUtils.getJavaRegexFromXSD("(\\\\\\\\\\\\p{IsBasicLatin}|\\p{IsTags})*+");
+ assertEquals("^(\\\\\\\\\\\\p{IsBasicLatin}|\\p{IsTags})*+$", fixedUnicodeScriptPattern);
// should throw exception
Pattern.compile(fixedUnicodeScriptPattern);
}