*/
package org.opendaylight.yangtools.yang.parser.stmt.rfc6020;
-import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.common.base.Splitter;
+import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
+import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
+import java.util.Map;
import java.util.Set;
import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.YangVersion;
import org.opendaylight.yangtools.yang.model.api.SchemaPath;
-import org.opendaylight.yangtools.yang.model.api.type.LengthConstraint;
-import org.opendaylight.yangtools.yang.model.api.type.RangeConstraint;
-import org.opendaylight.yangtools.yang.model.util.UnresolvedNumber;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+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.TypeEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.UnresolvedNumber;
+import org.opendaylight.yangtools.yang.model.api.stmt.ValueRange;
+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.UnionTypeDefinition;
+import org.opendaylight.yangtools.yang.parser.spi.meta.InferenceException;
import org.opendaylight.yangtools.yang.parser.spi.meta.QNameCacheNamespace;
import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
-import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.effective.type.LengthConstraintEffectiveImpl;
-import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.effective.type.RangeConstraintEffectiveImpl;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
/**
-* util class for manipulating YANG base and extended types implementation
+* Utility class for manipulating YANG base and extended types implementation.
*/
public final class TypeUtils {
public static final String UINT64 = "uint64";
public static final String UNION = "union";
- private static final Set<String> BUILT_IN_TYPES =
- ImmutableSet.of(BINARY, BITS, BOOLEAN, DECIMAL64, EMPTY, ENUMERATION, IDENTITY_REF, INSTANCE_IDENTIFIER,
- INT8, INT16, INT32, INT64, LEAF_REF, STRING, UINT8, UINT16, UINT32, UINT64, UNION);
+ private static final Map<String, String> BUILT_IN_TYPES = ImmutableMap.<String, String>builder()
+ .put(BINARY, BINARY)
+ .put(BITS, BITS)
+ .put(BOOLEAN, BOOLEAN)
+ .put(DECIMAL64, DECIMAL64)
+ .put(EMPTY, EMPTY)
+ .put(ENUMERATION, ENUMERATION)
+ .put(IDENTITY_REF,IDENTITY_REF)
+ .put(INSTANCE_IDENTIFIER, INSTANCE_IDENTIFIER)
+ .put(INT8, INT8)
+ .put(INT16, INT16)
+ .put(INT32, INT32)
+ .put(INT64, INT64)
+ .put(LEAF_REF, LEAF_REF)
+ .put(STRING, STRING)
+ .put(UINT8, UINT8)
+ .put(UINT16, UINT16)
+ .put(UINT32, UINT32)
+ .put(UINT64, UINT64)
+ .put(UNION, UNION)
+ .build();
private static final Set<String> TYPE_BODY_STMTS = ImmutableSet.of(
DECIMAL64, ENUMERATION, LEAF_REF, IDENTITY_REF, BITS, UNION);
return new BigDecimal(num1.toString()).compareTo(new BigDecimal(num2.toString()));
}
- private static Number parseIntegerConstraintValue(final String value) {
+ private static Number parseIntegerConstraintValue(final StmtContext<?, ?, ?> ctx, final String value) {
if ("max".equals(value)) {
return UnresolvedNumber.max();
}
try {
return new BigInteger(value);
- } catch (NumberFormatException e) {
- throw new IllegalArgumentException(String.format("Value %s is not a valid integer", value), e);
+ } catch (final NumberFormatException e) {
+ throw new SourceException(ctx.getStatementSourceReference(), e, "Value %s is not a valid integer", value);
}
}
- private static Number parseDecimalConstraintValue(final String value) {
+ private static Number parseDecimalConstraintValue(final StmtContext<?, ?, ?> ctx, final String value) {
if ("max".equals(value)) {
return UnresolvedNumber.max();
}
try {
return value.indexOf('.') != -1 ? new BigDecimal(value) : new BigInteger(value);
- } catch (NumberFormatException e) {
- throw new IllegalArgumentException(String.format("Value %s is not a valid decimal number", value), e);
+ } catch (final NumberFormatException e) {
+ throw new SourceException(String.format("Value %s is not a valid decimal number", value),
+ ctx.getStatementSourceReference(), e);
}
}
- public static List<RangeConstraint> parseRangeListFromString(final String rangeArgument) {
-
- Optional<String> description = Optional.absent();
- Optional<String> reference = Optional.absent();
-
- List<RangeConstraint> rangeConstraints = new ArrayList<>();
+ public static List<ValueRange> parseRangeListFromString(final StmtContext<?, ?, ?> ctx,
+ final String rangeArgument) {
+ final List<ValueRange> ranges = new ArrayList<>();
for (final String singleRange : PIPE_SPLITTER.split(rangeArgument)) {
- final Iterator<String> boundaries = TWO_DOTS_SPLITTER.splitToList(singleRange).iterator();
- final Number min = parseDecimalConstraintValue(boundaries.next());
+ final Iterator<String> boundaries = TWO_DOTS_SPLITTER.split(singleRange).iterator();
+ final Number min = parseDecimalConstraintValue(ctx, boundaries.next());
final Number max;
if (boundaries.hasNext()) {
- max = parseDecimalConstraintValue(boundaries.next());
+ max = parseDecimalConstraintValue(ctx, boundaries.next());
// if min larger than max then error
- if (compareNumbers(min, max) == 1) {
- throw new IllegalArgumentException(String.format(
- "Range constraint %s has descending order of boundaries; should be ascending", singleRange));
- }
- if (boundaries.hasNext()) {
- throw new IllegalArgumentException("Wrong number of boundaries in range constraint " + singleRange);
- }
+ SourceException.throwIf(compareNumbers(min, max) == 1, ctx.getStatementSourceReference(),
+ "Range constraint %s has descending order of boundaries; should be ascending", singleRange);
+ SourceException.throwIf(boundaries.hasNext(), ctx.getStatementSourceReference(),
+ "Wrong number of boundaries in range constraint %s", singleRange);
} else {
max = min;
}
// some of intervals overlapping
- if (rangeConstraints.size() > 1 && compareNumbers(min, Iterables.getLast(rangeConstraints).getMax()) != 1) {
- throw new IllegalArgumentException(String.format("Some of the ranges in %s are not disjoint",
- rangeArgument));
- }
-
- rangeConstraints.add(new RangeConstraintEffectiveImpl(min, max, description, reference));
+ InferenceException.throwIf(ranges.size() > 1
+ && compareNumbers(min, Iterables.getLast(ranges).upperBound()) != 1,
+ ctx.getStatementSourceReference(), "Some of the value ranges in %s are not disjoint",
+ rangeArgument);
+ ranges.add(ValueRange.of(min, max));
}
- return rangeConstraints;
+ return ranges;
}
- public static List<LengthConstraint> parseLengthListFromString(final String rangeArgument) {
+ public static List<ValueRange> parseLengthListFromString(final StmtContext<?, ?, ?> ctx,
+ final String lengthArgument) {
+ final List<ValueRange> ranges = new ArrayList<>();
- Optional<String> description = Optional.absent();
- Optional<String> reference = Optional.absent();
-
- List<LengthConstraint> rangeConstraints = new ArrayList<>();
-
- for (final String singleRange : PIPE_SPLITTER.split(rangeArgument)) {
- final Iterator<String> boundaries = TWO_DOTS_SPLITTER.splitToList(singleRange).iterator();
- final Number min = parseIntegerConstraintValue(boundaries.next());
+ for (final String singleRange : PIPE_SPLITTER.split(lengthArgument)) {
+ final Iterator<String> boundaries = TWO_DOTS_SPLITTER.split(singleRange).iterator();
+ final Number min = parseIntegerConstraintValue(ctx, boundaries.next());
final Number max;
if (boundaries.hasNext()) {
- max = parseIntegerConstraintValue(boundaries.next());
+ max = parseIntegerConstraintValue(ctx, boundaries.next());
// if min larger than max then error
- Preconditions.checkArgument(compareNumbers(min, max) != 1,
- "Length constraint %s has descending order of boundaries; should be ascending", singleRange);
- Preconditions.checkArgument(!boundaries.hasNext(), "Wrong number of boundaries in length constraint %s",
- singleRange);
+ SourceException.throwIf(compareNumbers(min, max) == 1, ctx.getStatementSourceReference(),
+ "Length constraint %s has descending order of boundaries; should be ascending.", singleRange);
+ SourceException.throwIf(boundaries.hasNext(), ctx.getStatementSourceReference(),
+ "Wrong number of boundaries in length constraint %s.", singleRange);
} else {
max = min;
}
// some of intervals overlapping
- if (rangeConstraints.size() > 1 && compareNumbers(min, Iterables.getLast(rangeConstraints).getMax()) != 1) {
- throw new IllegalArgumentException(String.format("Some of the length ranges in %s are not disjoint",
- rangeArgument));
- }
-
- rangeConstraints.add(new LengthConstraintEffectiveImpl(min, max, description, reference));
+ InferenceException.throwIf(ranges.size() > 1
+ && compareNumbers(min, Iterables.getLast(ranges).upperBound()) != 1,
+ ctx.getStatementSourceReference(), "Some of the length ranges in %s are not disjoint",
+ lengthArgument);
+ ranges.add(ValueRange.of(min, max));
}
- return rangeConstraints;
+ return ranges;
}
public static boolean isYangTypeBodyStmtString(final String typeName) {
}
public static boolean isYangBuiltInTypeString(final String typeName) {
- return BUILT_IN_TYPES.contains(typeName);
+ return BUILT_IN_TYPES.containsKey(typeName);
}
public static SchemaPath typeEffectiveSchemaPath(final StmtContext<?, ?, ?> stmtCtx) {
final SchemaPath path = stmtCtx.getSchemaPath().get();
+ final SchemaPath parent = path.getParent();
+ final QName parentQName = parent.getLastComponent();
+ Preconditions.checkArgument(parentQName != null, "Path %s has an empty parent", path);
+
final QName qname = stmtCtx.getFromNamespace(QNameCacheNamespace.class,
- QName.create(path.getParent().getLastComponent(), path.getLastComponent().getLocalName()));
- return path.getParent().createChild(qname);
+ QName.create(parentQName, path.getLastComponent().getLocalName()));
+ return parent.createChild(qname);
+ }
+
+ /**
+ * Checks whether supplied type has any of specified default values marked
+ * with an if-feature. This method creates mutable copy of supplied set of
+ * default values.
+ *
+ * @param yangVersion
+ * yang version
+ * @param typeStmt
+ * type statement which should be checked
+ * @param defaultValues
+ * set of default values which should be checked. The method
+ * creates mutable copy of this set
+ *
+ * @return true if any of specified default values is marked with an
+ * if-feature, otherwise false
+ */
+ public static boolean hasDefaultValueMarkedWithIfFeature(final YangVersion yangVersion,
+ final TypeEffectiveStatement<?> typeStmt, final Set<String> defaultValues) {
+ return !defaultValues.isEmpty() && yangVersion == YangVersion.VERSION_1_1
+ && isRelevantForIfFeatureCheck(typeStmt)
+ && isAnyDefaultValueMarkedWithIfFeature(typeStmt, new HashSet<>(defaultValues));
+ }
+
+ /**
+ * Checks whether supplied type has specified default value marked with an
+ * if-feature. This method creates mutable set of supplied default value.
+ *
+ * @param yangVersion
+ * yang version
+ * @param typeStmt
+ * type statement which should be checked
+ * @param defaultValue
+ * default value to be checked
+ *
+ * @return true if specified default value is marked with an if-feature,
+ * otherwise false
+ */
+ public static boolean hasDefaultValueMarkedWithIfFeature(final YangVersion yangVersion,
+ final TypeEffectiveStatement<?> typeStmt, final String defaultValue) {
+ final HashSet<String> defaultValues = new HashSet<>();
+ defaultValues.add(defaultValue);
+ return !Strings.isNullOrEmpty(defaultValue) && yangVersion == YangVersion.VERSION_1_1
+ && isRelevantForIfFeatureCheck(typeStmt)
+ && isAnyDefaultValueMarkedWithIfFeature(typeStmt, defaultValues);
+ }
+
+ static String findBuiltinString(final String rawArgument) {
+ return BUILT_IN_TYPES.get(rawArgument);
+ }
+
+ private static boolean isRelevantForIfFeatureCheck(final TypeEffectiveStatement<?> typeStmt) {
+ final TypeDefinition<?> typeDefinition = typeStmt.getTypeDefinition();
+ return typeDefinition instanceof EnumTypeDefinition || typeDefinition instanceof BitsTypeDefinition
+ || typeDefinition instanceof UnionTypeDefinition;
+ }
+
+ private static boolean isAnyDefaultValueMarkedWithIfFeature(final TypeEffectiveStatement<?> typeStmt,
+ final Set<String> defaultValues) {
+ final Iterator<? extends EffectiveStatement<?, ?>> iter = typeStmt.effectiveSubstatements().iterator();
+ while (iter.hasNext() && !defaultValues.isEmpty()) {
+ final EffectiveStatement<?, ?> effectiveSubstatement = iter.next();
+ if (YangStmtMapping.BIT.equals(effectiveSubstatement.statementDefinition())) {
+ final QName bitQName = (QName) effectiveSubstatement.argument();
+ if (defaultValues.remove(bitQName.getLocalName()) && containsIfFeature(effectiveSubstatement)) {
+ return true;
+ }
+ } else if (YangStmtMapping.ENUM.equals(effectiveSubstatement.statementDefinition())
+ && defaultValues.remove(effectiveSubstatement.argument())
+ && containsIfFeature(effectiveSubstatement)) {
+ return true;
+ } else if (effectiveSubstatement instanceof TypeEffectiveStatement && isAnyDefaultValueMarkedWithIfFeature(
+ (TypeEffectiveStatement<?>) effectiveSubstatement, defaultValues)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private static boolean containsIfFeature(final EffectiveStatement<?, ?> effectiveStatement) {
+ for (final EffectiveStatement<?, ?> effectiveSubstatement : effectiveStatement.effectiveSubstatements()) {
+ if (YangStmtMapping.IF_FEATURE.equals(effectiveSubstatement.statementDefinition())) {
+ return true;
+ }
+ }
+ return false;
}
}