Bug 6870: [Yang 1.1] Support for new "modifier" statement 58/49158/12
authorPeter Kajsa <pkajsa@cisco.com>
Thu, 8 Dec 2016 17:49:59 +0000 (18:49 +0100)
committerRobert Varga <rovarga@cisco.com>
Sat, 17 Dec 2016 13:47:12 +0000 (14:47 +0100)
The "modifier" statement, which is an optional substatement to the
"pattern" statement, takes as an argument the string "invert-match".
If a pattern has the "invert-match" modifier present, the type is
restricted to values that do not match the pattern.

Change-Id: I9e9d993a16077c1f3af1ab20c197d2e6c9e8c6af
Signed-off-by: Peter Kajsa <pkajsa@cisco.com>
Signed-off-by: Robert Varga <rovarga@cisco.com>
18 files changed:
yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/YangVersion.java
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/YangStmtMapping.java
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/type/ModifierKind.java
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/BaseConstraints.java
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/PatternConstraintImpl.java
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/spi/source/SourceException.java
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/rfc6020/PatternStatementImpl.java
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/rfc6020/YangInferencePipeline.java
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/rfc6020/YangVersionStatementImpl.java
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/rfc6020/effective/type/AbstractConstraintEffectiveStatement.java
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/rfc6020/effective/type/PatternConstraintEffectiveImpl.java
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/rfc7950/ModifierStatementImpl.java [new file with mode: 0644]
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/rfc7950/PatternStatementRfc7950Support.java [new file with mode: 0644]
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/rfc7950/effective/ModifierEffectiveStatementImpl.java [new file with mode: 0644]
yang/yang-parser-impl/src/test/java/org/opendaylight/yangtools/yang/parser/stmt/rfc7950/Bug6870Test.java [new file with mode: 0644]
yang/yang-parser-impl/src/test/resources/rfc7950/bug6870/foo.yang [new file with mode: 0644]
yang/yang-parser-impl/src/test/resources/rfc7950/bug6870/invalid10.yang [new file with mode: 0644]
yang/yang-parser-impl/src/test/resources/rfc7950/bug6870/invalid11.yang [new file with mode: 0644]

index a8b02c87774060ff3fcaecbe3455f7c0d7f67bca..59cb26a957555c81f1a0589f0f38a361292bd0da 100644 (file)
@@ -9,6 +9,10 @@ package org.opendaylight.yangtools.yang.common;
 
 import com.google.common.annotations.Beta;
 import com.google.common.base.Preconditions;
+import com.google.common.collect.Maps;
+import java.util.Arrays;
+import java.util.Map;
+import java.util.Optional;
 import javax.annotation.Nonnull;
 
 /**
@@ -27,6 +31,9 @@ public enum YangVersion {
      */
     VERSION_1_1("1.1", "RFC7950");
 
+    private static final Map<String, YangVersion> YANG_VERSION_MAP = Maps.uniqueIndex(Arrays.asList(values()),
+        YangVersion::toString);
+
     private final String str;
     private String reference;
 
@@ -40,18 +47,10 @@ public enum YangVersion {
      *
      * @param str String to parse
      * @return YANG version
-     * @throws IllegalArgumentException if the string is malformed
      * @throws NullPointerException if the string is null
      */
-    public static YangVersion parse(@Nonnull final String str) {
-        switch (str) {
-            case "1":
-                return VERSION_1;
-            case "1.1":
-                return VERSION_1_1;
-            default:
-                throw new IllegalArgumentException("Invalid YANG version '" + str + "'");
-        }
+    public static Optional<YangVersion> parse(@Nonnull final String str) {
+        return Optional.ofNullable(YANG_VERSION_MAP.get(Preconditions.checkNotNull(str)));
     }
 
     /**
index 0faed9616c844d464ef7db72e7c7e241a342e5ba..f087130e7cf67996b4b567b6adb929558bed3ec7 100644 (file)
@@ -53,6 +53,7 @@ import org.opendaylight.yangtools.yang.model.api.stmt.ListStatement;
 import org.opendaylight.yangtools.yang.model.api.stmt.MandatoryStatement;
 import org.opendaylight.yangtools.yang.model.api.stmt.MaxElementsStatement;
 import org.opendaylight.yangtools.yang.model.api.stmt.MinElementsStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ModifierStatement;
 import org.opendaylight.yangtools.yang.model.api.stmt.ModuleStatement;
 import org.opendaylight.yangtools.yang.model.api.stmt.MustStatement;
 import org.opendaylight.yangtools.yang.model.api.stmt.NamespaceStatement;
@@ -126,6 +127,7 @@ public enum YangStmtMapping implements StatementDefinition {
     MANDATORY(MandatoryStatement.class, "mandatory", "value"),
     MAX_ELEMENTS(MaxElementsStatement.class, "max-elements", "value"),
     MIN_ELEMENTS(MinElementsStatement.class, "min-elements", "value"),
+    MODIFIER(ModifierStatement.class, "modifier", "value"),
     MODULE(ModuleStatement.class, "module", "name"),
     MUST(MustStatement.class, "must", "condition"),
     NAMESPACE(NamespaceStatement.class, "namespace", "uri"),
index 5e4747a2355796858966fd099f91f5250cc22d9f..f83f46b489bf5582b0c93381f711a10139e1d98a 100644 (file)
@@ -9,23 +9,28 @@
 package org.opendaylight.yangtools.yang.model.api.type;
 
 import com.google.common.base.Preconditions;
+import com.google.common.collect.Maps;
+import java.util.Arrays;
+import java.util.Map;
+import java.util.Optional;
 import javax.annotation.Nonnull;
 
 /**
  * Enum describing the effect of a YANG modifier statement.
  *
- * As of YANG 1.1 (RFC7950) there is only one modifier value
- * available and that is "invert-match".
- * If there are more possible values added in the future,
+ * As of YANG 1.1 (RFC7950) there is only one modifier value available and that
+ * is "invert-match". If there are more possible values added in the future,
  * this enum can be extended with more enum constants.
  */
 public enum ModifierKind {
-
     INVERT_MATCH("invert-match");
 
+    private static final Map<String, ModifierKind> MODIFIER_KIND_MAP = Maps.uniqueIndex(
+        Arrays.asList(ModifierKind.values()), ModifierKind::getKeyword);
+
     private final String keyword;
 
-    ModifierKind(final String keyword) {
+    private ModifierKind(final String keyword) {
         this.keyword = Preconditions.checkNotNull(keyword);
     }
 
@@ -35,4 +40,16 @@ public enum ModifierKind {
     public @Nonnull String getKeyword() {
         return keyword;
     }
+
+    /**
+     * Returns ModifierKind based on supplied Yang keyword
+     *
+     * @param keyword
+     *            Yang keyword in string form
+     * @return ModifierKind based on supplied Yang keyword
+     * @throws NullPointerException if keyword is null
+     */
+    public static Optional<ModifierKind> parse(final String keyword) {
+        return Optional.ofNullable(MODIFIER_KIND_MAP.get(Preconditions.checkNotNull(keyword)));
+    }
 }
index 4283ae5aeb77ed774463f80e472c8e0f26f8e125..cef542935edc1e227e42319756b5fbbc26e857a7 100644 (file)
@@ -9,6 +9,7 @@ package org.opendaylight.yangtools.yang.model.util;
 
 import com.google.common.base.Optional;
 import org.opendaylight.yangtools.yang.model.api.type.LengthConstraint;
+import org.opendaylight.yangtools.yang.model.api.type.ModifierKind;
 import org.opendaylight.yangtools.yang.model.api.type.PatternConstraint;
 import org.opendaylight.yangtools.yang.model.api.type.RangeConstraint;
 
@@ -140,14 +141,18 @@ public final class BaseConstraints {
      * with additional behaviour:
      *
      * <ul>
-     * <li>{@link PatternConstraint#getErrorAppTag()} returns <code>invalid-regular-expression</code>
+     * <li>{@link PatternConstraint#getErrorAppTag()} returns
+     * <code>invalid-regular-expression</code>
      * </ul>
      *
      * @see PatternConstraint
      *
-     * @param pattern Regular expression, MUST NOT BE null.
-     * @param description Description associated with constraint.
-     * @param reference Reference associated with constraint.
+     * @param pattern
+     *            Regular expression, MUST NOT BE null.
+     * @param description
+     *            Description associated with constraint.
+     * @param reference
+     *            Reference associated with constraint.
      * @return Instance of {@link PatternConstraint}
      */
     public static PatternConstraint newPatternConstraint(final String pattern, final Optional<String> description,
@@ -162,20 +167,29 @@ public final class BaseConstraints {
      * with additional behaviour:
      *
      * <ul>
-     * <li>{@link PatternConstraint#getErrorAppTag()} returns <code>invalid-regular-expression</code>
+     * <li>{@link PatternConstraint#getErrorAppTag()} returns
+     * <code>invalid-regular-expression</code>
      * </ul>
      *
      * @see PatternConstraint
      *
-     * @param pattern Regular expression, MUST NOT BE null.
-     * @param description Description associated with constraint.
-     * @param reference Reference associated with constraint.
-     * @param errorAppTag error-app-tag associated with constraint.
-     * @param errorMessage error message associated with constraint.
+     * @param pattern
+     *            Regular expression, MUST NOT BE null.
+     * @param description
+     *            Description associated with constraint.
+     * @param reference
+     *            Reference associated with constraint.
+     * @param errorAppTag
+     *            error-app-tag associated with constraint.
+     * @param errorMessage
+     *            error message associated with constraint.
+     * @param modifier
+     *            Modifier of pattern constraint.
      * @return Instance of {@link PatternConstraint}
      */
     public static PatternConstraint newPatternConstraint(final String pattern, final Optional<String> description,
-            final Optional<String> reference, final String errorAppTag, final String errorMessage) {
-        return new PatternConstraintImpl(pattern, description, reference, errorAppTag, errorMessage);
+            final Optional<String> reference, final String errorAppTag, final String errorMessage,
+            final Optional<ModifierKind> modifier) {
+        return new PatternConstraintImpl(pattern, description, reference, errorAppTag, errorMessage, modifier);
     }
 }
index 390971f34903e34b18040ef7854ce3d957760c9e..be9715e9d89e87f312cfbb2ab7e70a8b1c769ae5 100644 (file)
@@ -7,10 +7,12 @@
  */
 package org.opendaylight.yangtools.yang.model.util;
 
+import com.google.common.base.MoreObjects;
 import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
 import java.util.Objects;
 import org.opendaylight.yangtools.concepts.Immutable;
+import org.opendaylight.yangtools.yang.model.api.type.ModifierKind;
 import org.opendaylight.yangtools.yang.model.api.type.PatternConstraint;
 
 /**
@@ -33,20 +35,22 @@ final class PatternConstraintImpl implements PatternConstraint, Immutable {
 
     private final String errorAppTag;
     private final String errorMessage;
+    private final ModifierKind modifier;
 
     PatternConstraintImpl(final String regex, final Optional<String> description, final Optional<String> reference) {
         this(regex, description, reference, "invalid-regular-expression", String.format(
-                "String %s is not valid regular expression.", regex));
+                "String %s is not valid regular expression.", regex), Optional.absent());
     }
 
     PatternConstraintImpl(final String regex, final Optional<String> description, final Optional<String> reference,
-            final String errorAppTag, final String errorMessage) {
+            final String errorAppTag, final String errorMessage, final Optional<ModifierKind> modifier) {
         this.regex = Preconditions.checkNotNull(regex, "regex must not be null.");
         this.description = description.orNull();
         this.reference = reference.orNull();
         this.errorAppTag = errorAppTag != null ? errorAppTag : "invalid-regular-expression";
         this.errorMessage = errorMessage != null ? errorMessage : String.format(
                 "String %s is not valid regular expression.", regex);
+        this.modifier = modifier.orNull();
     }
 
     @Override
@@ -76,14 +80,7 @@ final class PatternConstraintImpl implements PatternConstraint, Immutable {
 
     @Override
     public int hashCode() {
-        final int prime = 31;
-        int result = 1;
-        result = prime * result + Objects.hashCode(description);
-        result = prime * result + Objects.hashCode(errorAppTag);
-        result = prime * result + Objects.hashCode(errorMessage);
-        result = prime * result + Objects.hashCode(reference);
-        result = prime * result + regex.hashCode();
-        return result;
+        return Objects.hash(description, errorAppTag, errorMessage, reference, regex, modifier);
     }
 
     @Override
@@ -97,21 +94,13 @@ final class PatternConstraintImpl implements PatternConstraint, Immutable {
         final PatternConstraintImpl other = (PatternConstraintImpl) obj;
         return Objects.equals(description, other.description) && Objects.equals(errorAppTag, other.errorAppTag)
                 && Objects.equals(errorMessage, other.errorMessage) && Objects.equals(reference, other.reference)
-                && Objects.equals(regex, other.regex);
+                && Objects.equals(regex, other.regex) && Objects.equals(modifier, other.modifier);
     }
 
     @Override
     public String toString() {
-        return "PatternConstraintImpl [regex=" +
-                regex +
-                ", description=" +
-                description +
-                ", reference=" +
-                reference +
-                ", errorAppTag=" +
-                errorAppTag +
-                ", errorMessage=" +
-                errorMessage +
-                "]";
+        return MoreObjects.toStringHelper(this).add("regex", regex).add("description", description)
+                .add("reference", reference).add("errorAppTag", errorAppTag).add("errorMessage", errorMessage)
+                .add("modifier", modifier).toString();
     }
 }
\ No newline at end of file
index a31e549d63c85c894ef297d9ff4e4add7943f982..1a37a4bb08e7516a05b302b1c4324865dcb6fc1e 100644 (file)
@@ -8,6 +8,7 @@
 package org.opendaylight.yangtools.yang.parser.spi.source;
 
 import com.google.common.base.Preconditions;
+import java.util.Optional;
 import javax.annotation.Nonnull;
 
 /**
@@ -114,6 +115,23 @@ public class SourceException extends RuntimeException {
         return obj;
     }
 
+    /**
+     * Throw an instance of this exception if an optional is not present. If it is present, this method will return
+     * the unwrapped value.
+     *
+     * @param opt Optional to be checked
+     * @param source Statement source reference
+     * @param format Format string, according to {@link String#format(String, Object...)}.
+     * @param args Format string arguments, according to {@link String#format(String, Object...)}
+     * @return Object unwrapped from the opt optional
+     * @throws SourceException if the optional is not present
+     */
+    @Nonnull public static <T> T unwrap(final Optional<T> opt, @Nonnull final StatementSourceReference source,
+            @Nonnull final String format, final Object... args) {
+        throwIf(!opt.isPresent(), source, format, args);
+        return opt.get();
+    }
+
     private static String createMessage(@Nonnull final String message, @Nonnull final StatementSourceReference source) {
         Preconditions.checkNotNull(message);
         Preconditions.checkNotNull(source);
index d1db29a9b1f3b53fa4b634b32af0cb7958bcdad7..4b59ae72cee2bc5baf521110c1d9b604033046a0 100644 (file)
@@ -16,6 +16,7 @@ import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
 import org.opendaylight.yangtools.yang.model.api.stmt.DescriptionStatement;
 import org.opendaylight.yangtools.yang.model.api.stmt.ErrorAppTagStatement;
 import org.opendaylight.yangtools.yang.model.api.stmt.ErrorMessageStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ModifierStatement;
 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;
@@ -56,7 +57,7 @@ public class PatternStatementImpl extends AbstractDeclaredStatement<PatternConst
 
             try {
                 Pattern.compile(pattern);
-            } catch (PatternSyntaxException e) {
+            } catch (final PatternSyntaxException e) {
                 LOG.debug("Pattern \"{}\" failed to compile at {}", pattern, ctx.getStatementSourceReference(), e);
                 return null;
             }
@@ -103,6 +104,11 @@ public class PatternStatementImpl extends AbstractDeclaredStatement<PatternConst
         return firstDeclared(DescriptionStatement.class);
     }
 
+    @Override
+    public ModifierStatement getModifierStatement() {
+        return firstDeclared(ModifierStatement.class);
+    }
+
     @Override
     public ReferenceStatement getReference() {
         return firstDeclared(ReferenceStatement.class);
@@ -113,5 +119,4 @@ public class PatternStatementImpl extends AbstractDeclaredStatement<PatternConst
     public PatternConstraint getValue() {
         return argument();
     }
-
 }
index 4b746b2376912a498637b5410180929f6d488fb1..2e8585bf116d2635a5d0b2468dbd029ae2bdb20e 100644 (file)
@@ -74,9 +74,11 @@ import org.opendaylight.yangtools.yang.parser.stmt.rfc7950.ImportStatementRfc795
 import org.opendaylight.yangtools.yang.parser.stmt.rfc7950.IncludeStatementRfc7950Support;
 import org.opendaylight.yangtools.yang.parser.stmt.rfc7950.InputStatementRfc7950Support;
 import org.opendaylight.yangtools.yang.parser.stmt.rfc7950.ListStatementRfc7950Support;
+import org.opendaylight.yangtools.yang.parser.stmt.rfc7950.ModifierStatementImpl;
 import org.opendaylight.yangtools.yang.parser.stmt.rfc7950.ModuleStatementRfc7950Support;
 import org.opendaylight.yangtools.yang.parser.stmt.rfc7950.NotificationStatementRfc7950Support;
 import org.opendaylight.yangtools.yang.parser.stmt.rfc7950.OutputStatementRfc7950Support;
+import org.opendaylight.yangtools.yang.parser.stmt.rfc7950.PatternStatementRfc7950Support;
 import org.opendaylight.yangtools.yang.parser.stmt.rfc7950.RefineStatementRfc7950Support;
 import org.opendaylight.yangtools.yang.parser.stmt.rfc7950.SubmoduleStatementRfc7950Support;
 
@@ -165,7 +167,9 @@ public final class YangInferencePipeline {
             .addVersionSpecificSupport(VERSION_1, new EnumStatementImpl.Definition())
             .addVersionSpecificSupport(VERSION_1_1, new EnumStatementRfc7950Support())
             .addSupport(new LengthStatementImpl.Definition())
-            .addSupport(new PatternStatementImpl.Definition())
+            .addVersionSpecificSupport(VERSION_1, new PatternStatementImpl.Definition())
+            .addVersionSpecificSupport(VERSION_1_1, new PatternStatementRfc7950Support())
+            .addVersionSpecificSupport(VERSION_1_1, new ModifierStatementImpl.Definition())
             .addSupport(new RangeStatementImpl.Definition())
             .addVersionSpecificSupport(VERSION_1, new ContainerStatementImpl.Definition())
             .addVersionSpecificSupport(VERSION_1_1, new ContainerStatementRfc7950Support())
index 3662667cb932ed5824840e5835485a4fe7f96848..c52c6eee287efe7e2f61085c162b1413f33a3835 100644 (file)
@@ -37,11 +37,8 @@ public class YangVersionStatementImpl extends AbstractDeclaredStatement<YangVers
 
         @Override
         public YangVersion parseArgumentValue(final StmtContext<?, ?, ?> ctx, final String value) {
-            try {
-                return YangVersion.parse(value);
-            } catch (IllegalArgumentException e) {
-                throw new SourceException("Unsupported YANG version " + value, ctx.getStatementSourceReference(), e);
-            }
+            return SourceException.unwrap(YangVersion.parse(value), ctx.getStatementSourceReference(),
+                "Unsupported YANG version %s", value);
         }
 
         @Override
index 9930eab77c405256d0bf51d61fcc59dce4b83537..a852e4e8390d2eb3dbdfdb31d57942b52316fde7 100644 (file)
@@ -13,6 +13,7 @@ import java.util.List;
 import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
 import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
 import org.opendaylight.yangtools.yang.model.api.type.LengthConstraint;
+import org.opendaylight.yangtools.yang.model.api.type.ModifierKind;
 import org.opendaylight.yangtools.yang.model.api.type.PatternConstraint;
 import org.opendaylight.yangtools.yang.model.api.type.RangeConstraint;
 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
@@ -21,6 +22,7 @@ import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.effective.Description
 import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.effective.ErrorAppTagEffectiveStatementImpl;
 import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.effective.ErrorMessageEffectiveStatementImpl;
 import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.effective.ReferenceEffectiveStatementImpl;
+import org.opendaylight.yangtools.yang.parser.stmt.rfc7950.effective.ModifierEffectiveStatementImpl;
 
 abstract class AbstractConstraintEffectiveStatement<A, D extends DeclaredStatement<A>> extends
         DeclaredEffectiveStatementBase<A, D> {
@@ -28,16 +30,19 @@ abstract class AbstractConstraintEffectiveStatement<A, D extends DeclaredStateme
     private final String reference;
     private final String errorAppTag;
     private final String errorMessage;
+    private final ModifierKind modifier;
     private final A constraints;
 
-    public AbstractConstraintEffectiveStatement(final StmtContext<A, D, ?> ctx, final ConstraintFactory<A> constraintFactory) {
+    public AbstractConstraintEffectiveStatement(final StmtContext<A, D, ?> ctx,
+            final ConstraintFactory<A> constraintFactory) {
         super(ctx);
         String descriptionInit = null;
         String referenceInit = null;
         String errorAppTagInit = null;
         String errorMessageInit = null;
+        ModifierKind modifierInit = null;
 
-        for (EffectiveStatement<?, ?> stmt : effectiveSubstatements()) {
+        for (final EffectiveStatement<?, ?> stmt : effectiveSubstatements()) {
             if (stmt instanceof DescriptionEffectiveStatementImpl) {
                 descriptionInit = ((DescriptionEffectiveStatementImpl) stmt).argument();
             }
@@ -50,12 +55,16 @@ abstract class AbstractConstraintEffectiveStatement<A, D extends DeclaredStateme
             if (stmt instanceof ErrorMessageEffectiveStatementImpl) {
                 errorMessageInit = ((ErrorMessageEffectiveStatementImpl) stmt).argument();
             }
+            if (stmt instanceof ModifierEffectiveStatementImpl) {
+                modifierInit = ((ModifierEffectiveStatementImpl) stmt).argument();
+            }
         }
 
         this.description = descriptionInit;
         this.reference = referenceInit;
         this.errorAppTag = errorAppTagInit;
         this.errorMessage = errorMessageInit;
+        this.modifier = modifierInit;
         this.constraints = constraintFactory.createConstraints(this, super.argument());
     }
 
@@ -66,13 +75,17 @@ abstract class AbstractConstraintEffectiveStatement<A, D extends DeclaredStateme
 
     public final boolean isCustomizedStatement() {
         return this.description != null || this.reference != null || this.errorAppTag != null
-                || this.errorMessage != null;
+                || this.errorMessage != null || this.modifier != null;
     }
 
     public final String getDescription() {
         return description;
     }
 
+    public final ModifierKind getModifier() {
+        return modifier;
+    }
+
     public final String getReference() {
         return reference;
     }
@@ -92,13 +105,14 @@ abstract class ConstraintFactory<A> {
 
 abstract class ListConstraintFactory<A> extends ConstraintFactory<List<A>> {
     @Override
-    protected List<A> createConstraints(final AbstractConstraintEffectiveStatement<List<A>, ?> stmt, final List<A> argument) {
+    protected List<A> createConstraints(final AbstractConstraintEffectiveStatement<List<A>, ?> stmt,
+            final List<A> argument) {
         if (!stmt.isCustomizedStatement()) {
             return ImmutableList.copyOf(argument);
         }
 
         final List<A> customizedConstraints = new ArrayList<>(argument.size());
-        for (A constraint : argument) {
+        for (final A constraint : argument) {
             customizedConstraints.add(createCustomizedConstraint(constraint, stmt));
         }
         return ImmutableList.copyOf(customizedConstraints);
@@ -127,7 +141,8 @@ final class RangeConstraintFactory extends ListConstraintFactory<RangeConstraint
 
 final class PatternConstraintFactory extends ConstraintFactory<PatternConstraint> {
     @Override
-    protected PatternConstraint createConstraints(final AbstractConstraintEffectiveStatement<PatternConstraint, ?> stmt, final PatternConstraint argument) {
+    protected PatternConstraint createConstraints(
+            final AbstractConstraintEffectiveStatement<PatternConstraint, ?> stmt, final PatternConstraint argument) {
         if (!stmt.isCustomizedStatement()) {
             return argument;
         }
@@ -138,6 +153,6 @@ final class PatternConstraintFactory extends ConstraintFactory<PatternConstraint
     private static PatternConstraint createCustomizedConstraint(final PatternConstraint patternConstraint,
             final AbstractConstraintEffectiveStatement<?, ?> stmt) {
         return new PatternConstraintEffectiveImpl(patternConstraint.getRegularExpression(), stmt.getDescription(),
-                stmt.getReference(), stmt.getErrorAppTag(), stmt.getErrorMessage());
+                stmt.getReference(), stmt.getErrorAppTag(), stmt.getErrorMessage(), stmt.getModifier());
     }
 }
\ No newline at end of file
index a05ba0728afab711956bb8b55d48e2f45f42d4a5..e890bfca1eed74ff080e9109dd758a975cd30857 100644 (file)
@@ -7,9 +7,11 @@
  */
 package org.opendaylight.yangtools.yang.parser.stmt.rfc6020.effective.type;
 
+import com.google.common.base.MoreObjects;
 import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
 import java.util.Objects;
+import org.opendaylight.yangtools.yang.model.api.type.ModifierKind;
 import org.opendaylight.yangtools.yang.model.api.type.PatternConstraint;
 
 public class PatternConstraintEffectiveImpl implements PatternConstraint {
@@ -19,15 +21,16 @@ public class PatternConstraintEffectiveImpl implements PatternConstraint {
     private final String reference;
     private final String errorAppTag;
     private final String errorMessage;
+    private final ModifierKind modifier;
 
     public PatternConstraintEffectiveImpl(final String regex, final Optional<String> description,
             final Optional<String> reference) {
         this(regex, description.orNull(), reference.orNull(), "invalid-regular-expression", String.format(
-                "String %s is not valid regular expression.", regex));
+                "String %s is not valid regular expression.", regex), null);
     }
 
     public PatternConstraintEffectiveImpl(final String regex, final String description, final String reference,
-            final String errorAppTag, final String errorMessage) {
+            final String errorAppTag, final String errorMessage, final ModifierKind modifier) {
         super();
         this.regEx = Preconditions.checkNotNull(regex, "regex must not be null.");
         this.description = description;
@@ -35,6 +38,7 @@ public class PatternConstraintEffectiveImpl implements PatternConstraint {
         this.errorAppTag = errorAppTag != null ? errorAppTag : "invalid-regular-expression";
         this.errorMessage = errorMessage != null ? errorMessage : String.format(
                 "String %s is not valid regular expression.", regex);
+        this.modifier = modifier;
     }
 
     @Override
@@ -57,6 +61,11 @@ public class PatternConstraintEffectiveImpl implements PatternConstraint {
         return errorMessage;
     }
 
+    @Override
+    public ModifierKind getModifier() {
+        return modifier;
+    }
+
     @Override
     public String getReference() {
         return reference;
@@ -64,14 +73,7 @@ public class PatternConstraintEffectiveImpl implements PatternConstraint {
 
     @Override
     public int hashCode() {
-        final int prime = 31;
-        int result = 1;
-        result = prime * result + Objects.hashCode(description);
-        result = prime * result + Objects.hashCode(errorAppTag);
-        result = prime * result + Objects.hashCode(errorMessage);
-        result = prime * result + Objects.hashCode(reference);
-        result = prime * result + regEx.hashCode();
-        return result;
+        return Objects.hash(description, errorAppTag, errorMessage, reference, regEx, modifier);
     }
 
     @Override
@@ -86,28 +88,15 @@ public class PatternConstraintEffectiveImpl implements PatternConstraint {
             return false;
         }
         final PatternConstraintEffectiveImpl other = (PatternConstraintEffectiveImpl) obj;
-        if (!Objects.equals(description, other.description)) {
-            return false;
-        }
-        if (!Objects.equals(errorAppTag, other.errorAppTag)) {
-            return false;
-        }
-        if (!Objects.equals(errorMessage, other.errorMessage)) {
-            return false;
-        }
-        if (!Objects.equals(reference, other.reference)) {
-            return false;
-        }
-        if (!Objects.equals(regEx, other.regEx)) {
-            return false;
-        }
-        return true;
+        return Objects.equals(description, other.description) && Objects.equals(errorAppTag, other.errorAppTag)
+                && Objects.equals(errorMessage, other.errorMessage) && Objects.equals(reference, other.reference)
+                && Objects.equals(regEx, other.regEx) && Objects.equals(modifier, other.modifier);
     }
 
     @Override
     public String toString() {
-        return PatternConstraintEffectiveImpl.class.getSimpleName() + " [regex=" + regEx + ", description="
-                + description + ", reference=" + reference + ", errorAppTag=" + errorAppTag + ", errorMessage="
-                + errorMessage + "]";
+        return MoreObjects.toStringHelper(this).add("regex", regEx).add("description", description)
+                .add("reference", reference).add("errorAppTag", errorAppTag).add("errorMessage", errorMessage)
+                .add("modifier", modifier).toString();
     }
 }
diff --git a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/rfc7950/ModifierStatementImpl.java b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/rfc7950/ModifierStatementImpl.java
new file mode 100644 (file)
index 0000000..99f7172
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2016 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.rfc7950;
+
+import com.google.common.annotations.Beta;
+import javax.annotation.Nonnull;
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ModifierStatement;
+import org.opendaylight.yangtools.yang.model.api.type.ModifierKind;
+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;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+import org.opendaylight.yangtools.yang.parser.stmt.rfc7950.effective.ModifierEffectiveStatementImpl;
+
+/**
+ * Class providing necessary support for processing YANG 1.1 Modifier statement.
+ */
+@Beta
+public final class ModifierStatementImpl extends AbstractDeclaredStatement<ModifierKind> implements ModifierStatement {
+    private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator.builder(
+            YangStmtMapping.MODIFIER).build();
+
+    protected ModifierStatementImpl(final StmtContext<ModifierKind, ModifierStatement, ?> context) {
+        super(context);
+    }
+
+    public static class Definition
+            extends
+            AbstractStatementSupport<ModifierKind, ModifierStatement, EffectiveStatement<ModifierKind, ModifierStatement>> {
+
+        public Definition() {
+            super(YangStmtMapping.MODIFIER);
+        }
+
+        @Override
+        public ModifierKind parseArgumentValue(final StmtContext<?, ?, ?> ctx, final String value) {
+            return SourceException.unwrap(ModifierKind.parse(value), ctx.getStatementSourceReference(),
+                "'%s' is not valid argument of modifier statement", value);
+        }
+
+        @Override
+        public ModifierStatement createDeclared(final StmtContext<ModifierKind, ModifierStatement, ?> ctx) {
+            return new ModifierStatementImpl(ctx);
+        }
+
+        @Override
+        public EffectiveStatement<ModifierKind, ModifierStatement> createEffective(
+                final StmtContext<ModifierKind, ModifierStatement, EffectiveStatement<ModifierKind, ModifierStatement>> ctx) {
+            return new ModifierEffectiveStatementImpl(ctx);
+        }
+
+        @Override
+        public void onFullDefinitionDeclared(
+                final StmtContext.Mutable<ModifierKind, ModifierStatement, EffectiveStatement<ModifierKind, ModifierStatement>> stmt) {
+            super.onFullDefinitionDeclared(stmt);
+            getSubstatementValidator().validate(stmt);
+        }
+
+        @Override
+        protected SubstatementValidator getSubstatementValidator() {
+            return SUBSTATEMENT_VALIDATOR;
+        }
+    }
+
+    @Nonnull
+    @Override
+    public ModifierKind getValue() {
+        return argument();
+    }
+}
diff --git a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/rfc7950/PatternStatementRfc7950Support.java b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/rfc7950/PatternStatementRfc7950Support.java
new file mode 100644 (file)
index 0000000..b0e1cb7
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2016 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.rfc7950;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.parser.spi.SubstatementValidator;
+import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.PatternStatementImpl;
+
+/**
+ * Class providing necessary support for processing YANG 1.1 Pattern statement.
+ */
+@Beta
+public final class PatternStatementRfc7950Support extends PatternStatementImpl.Definition {
+    private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator.builder(YangStmtMapping
+            .PATTERN)
+            .addOptional(YangStmtMapping.DESCRIPTION)
+            .addOptional(YangStmtMapping.ERROR_APP_TAG)
+            .addOptional(YangStmtMapping.ERROR_MESSAGE)
+            .addOptional(YangStmtMapping.MODIFIER)
+            .addOptional(YangStmtMapping.REFERENCE)
+            .build();
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return SUBSTATEMENT_VALIDATOR;
+    }
+}
diff --git a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/rfc7950/effective/ModifierEffectiveStatementImpl.java b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/rfc7950/effective/ModifierEffectiveStatementImpl.java
new file mode 100644 (file)
index 0000000..423c782
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2016 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.rfc7950.effective;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.MoreObjects;
+import org.opendaylight.yangtools.yang.model.api.stmt.ModifierStatement;
+import org.opendaylight.yangtools.yang.model.api.type.ModifierKind;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.effective.DeclaredEffectiveStatementBase;
+
+/**
+ * YANG 1.1 Modifier effective statement implementation.
+ */
+@Beta
+public final class ModifierEffectiveStatementImpl extends
+        DeclaredEffectiveStatementBase<ModifierKind, ModifierStatement> {
+    public ModifierEffectiveStatementImpl(final StmtContext<ModifierKind, ModifierStatement, ?> ctx) {
+        super(ctx);
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this).add("argument", argument()).toString();
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-impl/src/test/java/org/opendaylight/yangtools/yang/parser/stmt/rfc7950/Bug6870Test.java b/yang/yang-parser-impl/src/test/java/org/opendaylight/yangtools/yang/parser/stmt/rfc7950/Bug6870Test.java
new file mode 100644 (file)
index 0000000..e1fb130
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2016 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.rfc7950;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import java.io.FileNotFoundException;
+import java.net.URISyntaxException;
+import java.util.List;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.ModifierKind;
+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.SchemaContextUtil;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SomeModifiersUnresolvedException;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+import org.opendaylight.yangtools.yang.stmt.StmtTestUtils;
+
+public class Bug6870Test {
+    private static final String FOO_NS = "foo";
+    private static final String FOO_REV = "1970-01-01";
+
+    @Test
+    public void valid11Test() throws ReactorException, SourceException, FileNotFoundException, URISyntaxException {
+        final SchemaContext schemaContext = StmtTestUtils.parseYangSource("/rfc7950/bug6870/foo.yang");
+        assertNotNull(schemaContext);
+
+        assertModifier(schemaContext, ModifierKind.INVERT_MATCH, ImmutableList.of("root", "my-leaf"));
+        assertModifier(schemaContext, null, ImmutableList.of("root", "my-leaf-2"));
+    }
+
+    private void assertModifier(final SchemaContext schemaContext, final ModifierKind expectedModifierKind,
+            final List<String> localNamePath) {
+        final SchemaNode findNode = findNode(schemaContext, localNamePath);
+        assertTrue(findNode instanceof LeafSchemaNode);
+        final LeafSchemaNode myLeaf = (LeafSchemaNode) findNode;
+
+        final TypeDefinition<? extends TypeDefinition<?>> type = myLeaf.getType();
+        assertTrue(type instanceof StringTypeDefinition);
+        final StringTypeDefinition stringType = (StringTypeDefinition) type;
+
+        final List<PatternConstraint> patternConstraints = stringType.getPatternConstraints();
+        assertEquals(1, patternConstraints.size());
+
+        final PatternConstraint patternConstraint = patternConstraints.iterator().next();
+        assertEquals(expectedModifierKind, patternConstraint.getModifier());
+    }
+
+    private static SchemaNode findNode(final SchemaContext context, final Iterable<String> localNamesPath) {
+        final Iterable<QName> qNames = Iterables.transform(localNamesPath,
+                localName -> QName.create(FOO_NS, FOO_REV, localName));
+        return SchemaContextUtil.findDataSchemaNode(context, SchemaPath.create(qNames, true));
+    }
+
+    @Test
+    public void invalid11Test() throws ReactorException, SourceException, FileNotFoundException, URISyntaxException {
+        try {
+            StmtTestUtils.parseYangSource("/rfc7950/bug6870/invalid11.yang");
+            fail("Test should fail due to invalid Yang 1.1");
+        } catch (final SomeModifiersUnresolvedException e) {
+            assertTrue(e.getCause().getMessage()
+                    .startsWith("'Invert-match' is not valid argument of modifier statement"));
+        }
+    }
+
+    @Test
+    public void invalid10Test() throws ReactorException, SourceException, FileNotFoundException, URISyntaxException {
+        try {
+            StmtTestUtils.parseYangSource("/rfc7950/bug6870/invalid10.yang");
+            fail("Test should fail due to invalid Yang 1.0");
+        } catch (final SomeModifiersUnresolvedException e) {
+            assertTrue(e.getCause().getMessage().startsWith("modifier is not a YANG statement or use of extension"));
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-impl/src/test/resources/rfc7950/bug6870/foo.yang b/yang/yang-parser-impl/src/test/resources/rfc7950/bug6870/foo.yang
new file mode 100644 (file)
index 0000000..02752a0
--- /dev/null
@@ -0,0 +1,22 @@
+module foo {
+    namespace "foo";
+    prefix foo;
+    yang-version 1.1;
+
+    container root {
+        leaf my-leaf {
+            type string {
+                pattern '[xX][mM][lL].*' {
+                    modifier invert-match;
+                }
+            }
+        }
+        leaf my-leaf-2 {
+            type string {
+                pattern '[xX][mM][lL].*' {
+                    description "no modifier";
+                }
+            }
+        }
+    }
+}
diff --git a/yang/yang-parser-impl/src/test/resources/rfc7950/bug6870/invalid10.yang b/yang/yang-parser-impl/src/test/resources/rfc7950/bug6870/invalid10.yang
new file mode 100644 (file)
index 0000000..ce59371
--- /dev/null
@@ -0,0 +1,14 @@
+module foo {
+    namespace "foo";
+    prefix foo;
+
+    container root {
+        leaf my-leaf {
+            type string {
+                pattern '[xX][mM][lL].*' {
+                    modifier invert-match;
+                }
+            }
+        }
+    }
+}
diff --git a/yang/yang-parser-impl/src/test/resources/rfc7950/bug6870/invalid11.yang b/yang/yang-parser-impl/src/test/resources/rfc7950/bug6870/invalid11.yang
new file mode 100644 (file)
index 0000000..60b3672
--- /dev/null
@@ -0,0 +1,15 @@
+module foo {
+    namespace "foo";
+    prefix foo;
+    yang-version 1.1;
+
+    container root {
+        leaf my-leaf {
+            type string {
+                pattern '[xX][mM][lL].*' {
+                    modifier Invert-match;
+                }
+            }
+        }
+    }
+}