3aee37027267ebd1a1e1c8f3e7db7d3bb1b1d355
[yangtools.git] / yang / yang-parser-impl / src / main / java / org / opendaylight / yangtools / yang / parser / stmt / rfc6020 / TypeUtils.java
1 /*
2  * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.yangtools.yang.parser.stmt.rfc6020;
9
10 import com.google.common.base.Optional;
11 import com.google.common.base.Preconditions;
12 import com.google.common.base.Splitter;
13 import com.google.common.collect.ImmutableSet;
14 import com.google.common.collect.Iterables;
15 import java.math.BigDecimal;
16 import java.math.BigInteger;
17 import java.util.ArrayList;
18 import java.util.Iterator;
19 import java.util.List;
20 import java.util.Set;
21 import org.opendaylight.yangtools.yang.common.QName;
22 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
23 import org.opendaylight.yangtools.yang.model.api.type.LengthConstraint;
24 import org.opendaylight.yangtools.yang.model.api.type.RangeConstraint;
25 import org.opendaylight.yangtools.yang.model.util.UnresolvedNumber;
26 import org.opendaylight.yangtools.yang.parser.spi.meta.InferenceException;
27 import org.opendaylight.yangtools.yang.parser.spi.meta.QNameCacheNamespace;
28 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
29 import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
30 import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.effective.type.LengthConstraintEffectiveImpl;
31 import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.effective.type.RangeConstraintEffectiveImpl;
32
33 /**
34 * util class for manipulating YANG base and extended types implementation
35 */
36 public final class TypeUtils {
37
38     public static final String BINARY = "binary";
39     public static final String BITS = "bits";
40     public static final String BOOLEAN = "boolean";
41     public static final String DECIMAL64 = "decimal64";
42     public static final String EMPTY = "empty";
43     public static final String ENUMERATION = "enumeration";
44     public static final String IDENTITY_REF = "identityref";
45     public static final String INSTANCE_IDENTIFIER = "instance-identifier";
46     public static final String INT8 = "int8";
47     public static final String INT16 = "int16";
48     public static final String INT32 = "int32";
49     public static final String INT64 = "int64";
50     public static final String LEAF_REF = "leafref";
51     public static final String STRING = "string";
52     public static final String UINT8 = "uint8";
53     public static final String UINT16 = "uint16";
54     public static final String UINT32 = "uint32";
55     public static final String UINT64 = "uint64";
56     public static final String UNION = "union";
57
58     private static final Set<String> BUILT_IN_TYPES =
59             ImmutableSet.of(BINARY, BITS, BOOLEAN, DECIMAL64, EMPTY, ENUMERATION, IDENTITY_REF, INSTANCE_IDENTIFIER,
60                     INT8, INT16, INT32, INT64, LEAF_REF, STRING, UINT8, UINT16, UINT32, UINT64, UNION);
61
62     private static final Set<String> TYPE_BODY_STMTS = ImmutableSet.of(
63         DECIMAL64, ENUMERATION, LEAF_REF, IDENTITY_REF, BITS, UNION);
64     private static final Splitter PIPE_SPLITTER = Splitter.on('|').trimResults();
65     private static final Splitter TWO_DOTS_SPLITTER = Splitter.on("..").trimResults();
66
67     private TypeUtils() {
68     }
69
70     private static BigDecimal yangConstraintToBigDecimal(final Number number) {
71         if (UnresolvedNumber.max().equals(number)) {
72             return RangeStatementImpl.YANG_MAX_NUM;
73         }
74         if (UnresolvedNumber.min().equals(number)) {
75             return RangeStatementImpl.YANG_MIN_NUM;
76         }
77
78         return new BigDecimal(number.toString());
79     }
80
81     private static int compareNumbers(final Number n1, final Number n2) {
82
83         final BigDecimal num1 = yangConstraintToBigDecimal(n1);
84         final BigDecimal num2 = yangConstraintToBigDecimal(n2);
85
86         return new BigDecimal(num1.toString()).compareTo(new BigDecimal(num2.toString()));
87     }
88
89     private static Number parseIntegerConstraintValue(final StmtContext<?, ?, ?> ctx, final String value) {
90         if ("max".equals(value)) {
91             return UnresolvedNumber.max();
92         }
93         if ("min".equals(value)) {
94             return UnresolvedNumber.min();
95         }
96
97         try {
98             return new BigInteger(value);
99         } catch (NumberFormatException e) {
100             throw new SourceException(String.format("Value %s is not a valid integer", value),
101                     ctx.getStatementSourceReference(), e);
102         }
103     }
104
105     private static Number parseDecimalConstraintValue(final StmtContext<?, ?, ?> ctx, final String value) {
106         if ("max".equals(value)) {
107             return UnresolvedNumber.max();
108         }
109         if ("min".equals(value)) {
110             return UnresolvedNumber.min();
111         }
112
113         try {
114             return value.indexOf('.') != -1 ? new BigDecimal(value) : new BigInteger(value);
115         } catch (NumberFormatException e) {
116             throw new SourceException(String.format("Value %s is not a valid decimal number", value),
117                     ctx.getStatementSourceReference(), e);
118         }
119     }
120
121     public static List<RangeConstraint> parseRangeListFromString(final StmtContext<?, ?, ?> ctx,
122                                                                  final String rangeArgument) {
123
124         Optional<String> description = Optional.absent();
125         Optional<String> reference = Optional.absent();
126
127         List<RangeConstraint> rangeConstraints = new ArrayList<>();
128
129         for (final String singleRange : PIPE_SPLITTER.split(rangeArgument)) {
130             final Iterator<String> boundaries = TWO_DOTS_SPLITTER.splitToList(singleRange).iterator();
131             final Number min = parseDecimalConstraintValue(ctx, boundaries.next());
132
133             final Number max;
134             if (boundaries.hasNext()) {
135                 max = parseDecimalConstraintValue(ctx, boundaries.next());
136
137                 // if min larger than max then error
138                 if (compareNumbers(min, max) == 1) {
139                     throw new InferenceException(String.format(
140                             "Range constraint %s has descending order of boundaries; should be ascending",
141                             singleRange), ctx.getStatementSourceReference());
142                 }
143                 if (boundaries.hasNext()) {
144                     throw new SourceException(String.format("Wrong number of boundaries in range constraint %s",
145                             singleRange), ctx.getStatementSourceReference());
146                 }
147             } else {
148                 max = min;
149             }
150
151             // some of intervals overlapping
152             if (rangeConstraints.size() > 1 && compareNumbers(min, Iterables.getLast(rangeConstraints).getMax()) != 1) {
153                 throw new InferenceException(String.format("Some of the ranges in %s are not disjoint",
154                         rangeArgument), ctx.getStatementSourceReference());
155             }
156
157             rangeConstraints.add(new RangeConstraintEffectiveImpl(min, max, description, reference));
158         }
159
160         return rangeConstraints;
161     }
162
163     public static List<LengthConstraint> parseLengthListFromString(final StmtContext<?, ?, ?> ctx,
164             final String rangeArgument) {
165         Optional<String> description = Optional.absent();
166         Optional<String> reference = Optional.absent();
167
168         List<LengthConstraint> rangeConstraints = new ArrayList<>();
169
170         for (final String singleRange : PIPE_SPLITTER.split(rangeArgument)) {
171             final Iterator<String> boundaries = TWO_DOTS_SPLITTER.splitToList(singleRange).iterator();
172             final Number min = parseIntegerConstraintValue(ctx, boundaries.next());
173
174             final Number max;
175             if (boundaries.hasNext()) {
176                 max = parseIntegerConstraintValue(ctx, boundaries.next());
177
178                 // if min larger than max then error
179                 Preconditions.checkArgument(compareNumbers(min, max) != 1,
180                         "Length constraint %s has descending order of boundaries; should be ascending. Statement source at %s",
181                         singleRange, ctx.getStatementSourceReference());
182                 Preconditions.checkArgument(!boundaries.hasNext(),
183                         "Wrong number of boundaries in length constraint %s. Statement source at %s", singleRange,
184                         ctx.getStatementSourceReference());
185             } else {
186                 max = min;
187             }
188
189             // some of intervals overlapping
190             if (rangeConstraints.size() > 1 && compareNumbers(min, Iterables.getLast(rangeConstraints).getMax()) != 1) {
191                 throw new InferenceException(String.format("Some of the length ranges in %s are not disjoint",
192                         rangeArgument), ctx.getStatementSourceReference());
193             }
194
195             rangeConstraints.add(new LengthConstraintEffectiveImpl(min, max, description, reference));
196         }
197
198         return rangeConstraints;
199     }
200
201     public static boolean isYangTypeBodyStmtString(final String typeName) {
202         return TYPE_BODY_STMTS.contains(typeName);
203     }
204
205     public static boolean isYangBuiltInTypeString(final String typeName) {
206         return BUILT_IN_TYPES.contains(typeName);
207     }
208
209
210     public static SchemaPath typeEffectiveSchemaPath(final StmtContext<?, ?, ?> stmtCtx) {
211         final SchemaPath path = stmtCtx.getSchemaPath().get();
212         final QName qname = stmtCtx.getFromNamespace(QNameCacheNamespace.class,
213             QName.create(path.getParent().getLastComponent(), path.getLastComponent().getLocalName()));
214         return path.getParent().createChild(qname);
215     }
216 }