Cleanup TypeUtils
[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.ImmutableMap;
14 import com.google.common.collect.ImmutableMap.Builder;
15 import com.google.common.collect.ImmutableSet;
16 import com.google.common.collect.Iterables;
17 import java.math.BigDecimal;
18 import java.math.BigInteger;
19 import java.util.ArrayList;
20 import java.util.Collections;
21 import java.util.Comparator;
22 import java.util.Iterator;
23 import java.util.List;
24 import java.util.Map;
25 import java.util.Set;
26 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
27 import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
28 import org.opendaylight.yangtools.yang.model.api.type.LengthConstraint;
29 import org.opendaylight.yangtools.yang.model.api.type.RangeConstraint;
30 import org.opendaylight.yangtools.yang.model.util.BinaryType;
31 import org.opendaylight.yangtools.yang.model.util.BooleanType;
32 import org.opendaylight.yangtools.yang.model.util.EmptyType;
33 import org.opendaylight.yangtools.yang.model.util.Int16;
34 import org.opendaylight.yangtools.yang.model.util.Int32;
35 import org.opendaylight.yangtools.yang.model.util.Int64;
36 import org.opendaylight.yangtools.yang.model.util.Int8;
37 import org.opendaylight.yangtools.yang.model.util.StringType;
38 import org.opendaylight.yangtools.yang.model.util.Uint16;
39 import org.opendaylight.yangtools.yang.model.util.Uint32;
40 import org.opendaylight.yangtools.yang.model.util.Uint64;
41 import org.opendaylight.yangtools.yang.model.util.Uint8;
42 import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.effective.type.LengthConstraintEffectiveImpl;
43 import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.effective.type.RangeConstraintEffectiveImpl;
44 import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.effective.type.TypeDefinitionEffectiveBuilder;
45 import org.opendaylight.yangtools.yang.parser.util.UnknownBoundaryNumber;
46
47 /**
48 * util class for manipulating YANG base and extended types implementation
49 */
50 public final class TypeUtils {
51
52     public static final String BINARY = "binary";
53     public static final String BITS = "bits";
54     public static final String BOOLEAN = "boolean";
55     public static final String DECIMAL64 = "decimal64";
56     public static final String EMPTY = "empty";
57     public static final String ENUMERATION = "enumeration";
58     public static final String IDENTITY_REF = "identityref";
59     public static final String INSTANCE_IDENTIFIER = "instance-identifier";
60     public static final String INT8 = "int8";
61     public static final String INT16 = "int16";
62     public static final String INT32 = "int32";
63     public static final String INT64 = "int64";
64     public static final String LEAF_REF = "leafref";
65     public static final String STRING = "string";
66     public static final String UINT8 = "uint8";
67     public static final String UINT16 = "uint16";
68     public static final String UINT32 = "uint32";
69     public static final String UINT64 = "uint64";
70     public static final String UNION = "union";
71
72     private static final Set<String> BUILT_IN_TYPES = ImmutableSet.of(
73         BINARY, BITS, BOOLEAN, DECIMAL64, EMPTY, ENUMERATION, IDENTITY_REF, INSTANCE_IDENTIFIER,
74         INT8, INT16, INT32, INT64, LEAF_REF, STRING, UINT8, UINT16, UINT32, UINT64, UNION);
75
76     public static final Set<String> TYPE_BODY_STMTS = ImmutableSet.of(
77         DECIMAL64, ENUMERATION, LEAF_REF, IDENTITY_REF, INSTANCE_IDENTIFIER, BITS, UNION);
78
79     public static final Map<String, TypeDefinition<?>> PRIMITIVE_TYPES_MAP;
80     static {
81         Builder<String, TypeDefinition<?>> b = ImmutableMap.<String, TypeDefinition<?>>builder();
82         b.put(BINARY, BinaryType.getInstance());
83         b.put(BOOLEAN, BooleanType.getInstance());
84         b.put(EMPTY, EmptyType.getInstance());
85         b.put(INT8, Int8.getInstance());
86         b.put(INT16, Int16.getInstance());
87         b.put(INT32, Int32.getInstance());
88         b.put(INT64, Int64.getInstance());
89         b.put(STRING, StringType.getInstance());
90         b.put(UINT8, Uint8.getInstance());
91         b.put(UINT16, Uint16.getInstance());
92         b.put(UINT32, Uint32.getInstance());
93         b.put(UINT64, Uint64.getInstance());
94         PRIMITIVE_TYPES_MAP = b.build();
95     }
96
97     private static final Comparator<TypeDefinition<?>> TYPE_SORT_COMPARATOR = new Comparator<TypeDefinition<?>>() {
98         @Override
99         public int compare(final TypeDefinition<?> o1, final TypeDefinition<?> o2) {
100             return Boolean.compare(isBuiltInType(o2), isBuiltInType(o1));
101         }
102     };
103
104     private TypeUtils() {
105     }
106
107     private static final Splitter PIPE_SPLITTER = Splitter.on('|').trimResults();
108     private static final Splitter TWO_DOTS_SPLITTER = Splitter.on("..").trimResults();
109
110     private static BigDecimal yangConstraintToBigDecimal(final Number number) {
111         if (number instanceof UnknownBoundaryNumber) {
112             if (number.toString().equals("min")) {
113                 return RangeStatementImpl.YANG_MIN_NUM;
114             } else {
115                 return RangeStatementImpl.YANG_MAX_NUM;
116             }
117         } else {
118             return new BigDecimal(number.toString());
119         }
120     }
121
122     public static int compareNumbers(final Number n1, final Number n2) {
123
124         final BigDecimal num1 = yangConstraintToBigDecimal(n1);
125         final BigDecimal num2 = yangConstraintToBigDecimal(n2);
126
127         return new BigDecimal(num1.toString()).compareTo(new BigDecimal(num2.toString()));
128     }
129
130     private static Number parseIntegerConstraintValue(final String value) {
131         Number result;
132
133         if (isMinOrMaxString(value)) {
134             result = new UnknownBoundaryNumber(value);
135         } else {
136             try {
137                 result = new BigInteger(value);
138             } catch (NumberFormatException e) {
139                 throw new IllegalArgumentException(String.format("Value %s is not a valid integer", value), e);
140             }
141         }
142         return result;
143     }
144
145     private static Number parseDecimalConstraintValue(final String value) {
146         final Number result;
147
148         if (isMinOrMaxString(value)) {
149             result = new UnknownBoundaryNumber(value);
150         } else {
151             try {
152                 if (value.indexOf('.') != -1) {
153                     result = new BigDecimal(value);
154                 } else {
155                     result = new BigInteger(value);
156                 }
157             } catch (NumberFormatException e) {
158                 throw new IllegalArgumentException(String.format("Value %s is not a valid decimal number", value), e);
159             }
160         }
161         return result;
162     }
163
164     private static boolean isMinOrMaxString(final String value) {
165         return "min".equals(value) || "max".equals(value);
166     }
167
168     public static List<RangeConstraint> parseRangeListFromString(final String rangeArgument) {
169
170         Optional<String> description = Optional.absent();
171         Optional<String> reference = Optional.absent();
172
173         List<RangeConstraint> rangeConstraints = new ArrayList<>();
174
175         for (final String singleRange : PIPE_SPLITTER.split(rangeArgument)) {
176             final Iterator<String> boundaries = TWO_DOTS_SPLITTER.splitToList(singleRange).iterator();
177             final Number min = parseDecimalConstraintValue(boundaries.next());
178
179             final Number max;
180             if (boundaries.hasNext()) {
181                 max = parseDecimalConstraintValue(boundaries.next());
182
183                 // if min larger than max then error
184                 if (compareNumbers(min, max) == 1) {
185                     throw new IllegalArgumentException(String.format(
186                             "Range constraint %s has descending order of boundaries; should be ascending", singleRange));
187                 }
188                 if (boundaries.hasNext()) {
189                     throw new IllegalArgumentException("Wrong number of boundaries in range constraint " + singleRange);
190                 }
191             } else {
192                 max = min;
193             }
194
195             // some of intervals overlapping
196             if (rangeConstraints.size() > 1 && compareNumbers(min, Iterables.getLast(rangeConstraints).getMax()) != 1) {
197                 throw new IllegalArgumentException(String.format("Some of the ranges in %s are not disjoint",
198                         rangeArgument));
199             }
200
201             rangeConstraints.add(new RangeConstraintEffectiveImpl(min, max, description, reference));
202         }
203
204         return rangeConstraints;
205     }
206
207     public static List<LengthConstraint> parseLengthListFromString(final String rangeArgument) {
208
209         Optional<String> description = Optional.absent();
210         Optional<String> reference = Optional.absent();
211
212         List<LengthConstraint> rangeConstraints = new ArrayList<>();
213
214         for (final String singleRange : PIPE_SPLITTER.split(rangeArgument)) {
215             final Iterator<String> boundaries = TWO_DOTS_SPLITTER.splitToList(singleRange).iterator();
216             final Number min = parseIntegerConstraintValue(boundaries.next());
217
218             final Number max;
219             if (boundaries.hasNext()) {
220                 max = parseIntegerConstraintValue(boundaries.next());
221
222                 // if min larger than max then error
223                 Preconditions.checkArgument(compareNumbers(min, max) != 1,
224                         "Length constraint %s has descending order of boundaries; should be ascending", singleRange);
225                 Preconditions.checkArgument(!boundaries.hasNext(), "Wrong number of boundaries in length constraint %s",
226                         singleRange);
227             } else {
228                 max = min;
229             }
230
231             // some of intervals overlapping
232             if (rangeConstraints.size() > 1 && compareNumbers(min, Iterables.getLast(rangeConstraints).getMax()) != 1) {
233                 throw new IllegalArgumentException(String.format("Some of the length ranges in %s are not disjoint",
234                         rangeArgument));
235             }
236
237             rangeConstraints.add(new LengthConstraintEffectiveImpl(min, max, description, reference));
238         }
239
240         return rangeConstraints;
241     }
242
243     public static boolean isBuiltInType(final TypeDefinition<?> o1) {
244         return BUILT_IN_TYPES.contains(o1.getQName().getLocalName());
245     }
246
247     public static boolean isYangBuiltInTypeString(final String typeName) {
248         return BUILT_IN_TYPES.contains(typeName);
249     }
250
251     public static boolean isYangPrimitiveTypeString(final String typeName) {
252         return PRIMITIVE_TYPES_MAP.containsKey(typeName);
253     }
254
255     public static boolean isYangTypeBodyStmtString(final String typeName) {
256         return TYPE_BODY_STMTS.contains(typeName);
257     }
258
259     public static TypeDefinition<?> getYangPrimitiveTypeFromString(final String typeName) {
260         return PRIMITIVE_TYPES_MAP.get(typeName);
261     }
262
263     public static TypeDefinition<?> getTypeFromEffectiveStatement(final EffectiveStatement<?, ?> effectiveStatement) {
264         if (effectiveStatement instanceof TypeDefinitionEffectiveBuilder) {
265             TypeDefinitionEffectiveBuilder typeDefEffectiveBuilder = (TypeDefinitionEffectiveBuilder) effectiveStatement;
266             return typeDefEffectiveBuilder.buildType();
267         } else {
268             final String typeName = ((TypeDefinition<?>) effectiveStatement).getQName().getLocalName();
269             return PRIMITIVE_TYPES_MAP.get(typeName);
270         }
271     }
272
273     public static void sortTypes(final List<TypeDefinition<?>> typesInit) {
274         Collections.sort(typesInit, TYPE_SORT_COMPARATOR);
275     }
276 }