0155c8bb2422a140b164c38aed5840c1f7a36bc0
[yangtools.git] / yang / yang-parser-impl / src / main / java / org / opendaylight / yangtools / yang / parser / impl / ParserListenerUtils.java
1 /*
2  * Copyright (c) 2013 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/eplv10.html
7  */
8 package org.opendaylight.yangtools.yang.parser.impl;
9
10 import static com.google.common.base.Preconditions.checkState;
11
12 import com.google.common.base.CharMatcher;
13 import com.google.common.base.Optional;
14 import com.google.common.base.Splitter;
15 import com.google.common.collect.Lists;
16
17 import java.math.BigDecimal;
18 import java.math.BigInteger;
19 import java.net.URI;
20 import java.util.ArrayList;
21 import java.util.Collections;
22 import java.util.Date;
23 import java.util.Iterator;
24 import java.util.List;
25 import java.util.Stack;
26
27 import org.antlr.v4.runtime.ParserRuleContext;
28 import org.antlr.v4.runtime.tree.ParseTree;
29 import org.antlr.v4.runtime.tree.TerminalNode;
30 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser;
31 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Argument_stmtContext;
32 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Base_stmtContext;
33 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Bit_stmtContext;
34 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Bits_specificationContext;
35 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Config_argContext;
36 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Config_stmtContext;
37 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Decimal64_specificationContext;
38 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Default_stmtContext;
39 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Description_stmtContext;
40 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Enum_specificationContext;
41 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Enum_stmtContext;
42 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Error_app_tag_stmtContext;
43 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Error_message_stmtContext;
44 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Fraction_digits_stmtContext;
45 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Identityref_specificationContext;
46 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Instance_identifier_specificationContext;
47 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Key_stmtContext;
48 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Leafref_specificationContext;
49 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Length_stmtContext;
50 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Mandatory_argContext;
51 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Mandatory_stmtContext;
52 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Max_elements_stmtContext;
53 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Max_value_argContext;
54 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Min_elements_stmtContext;
55 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Min_value_argContext;
56 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Module_stmtContext;
57 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Must_stmtContext;
58 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Numerical_restrictionsContext;
59 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Ordered_by_argContext;
60 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Ordered_by_stmtContext;
61 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Path_stmtContext;
62 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Pattern_stmtContext;
63 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Position_stmtContext;
64 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Presence_stmtContext;
65 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Range_stmtContext;
66 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Reference_stmtContext;
67 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Refine_anyxml_stmtsContext;
68 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Refine_choice_stmtsContext;
69 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Refine_container_stmtsContext;
70 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Refine_leaf_list_stmtsContext;
71 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Refine_leaf_stmtsContext;
72 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Refine_list_stmtsContext;
73 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Refine_pomContext;
74 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Refine_stmtContext;
75 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Require_instance_argContext;
76 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Require_instance_stmtContext;
77 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Status_argContext;
78 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Status_stmtContext;
79 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.StringContext;
80 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.String_restrictionsContext;
81 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Type_body_stmtsContext;
82 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Units_stmtContext;
83 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Value_stmtContext;
84 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.When_stmtContext;
85 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Yin_element_argContext;
86 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Yin_element_stmtContext;
87 import org.opendaylight.yangtools.yang.common.QName;
88 import org.opendaylight.yangtools.yang.model.api.MustDefinition;
89 import org.opendaylight.yangtools.yang.model.api.RevisionAwareXPath;
90 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
91 import org.opendaylight.yangtools.yang.model.api.Status;
92 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
93 import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
94 import org.opendaylight.yangtools.yang.model.api.type.BinaryTypeDefinition;
95 import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition;
96 import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition.Bit;
97 import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition;
98 import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition.EnumPair;
99 import org.opendaylight.yangtools.yang.model.api.type.IntegerTypeDefinition;
100 import org.opendaylight.yangtools.yang.model.api.type.LengthConstraint;
101 import org.opendaylight.yangtools.yang.model.api.type.PatternConstraint;
102 import org.opendaylight.yangtools.yang.model.api.type.RangeConstraint;
103 import org.opendaylight.yangtools.yang.model.api.type.StringTypeDefinition;
104 import org.opendaylight.yangtools.yang.model.api.type.UnsignedIntegerTypeDefinition;
105 import org.opendaylight.yangtools.yang.model.util.BaseConstraints;
106 import org.opendaylight.yangtools.yang.model.util.BaseTypes;
107 import org.opendaylight.yangtools.yang.model.util.BinaryType;
108 import org.opendaylight.yangtools.yang.model.util.BitImpl;
109 import org.opendaylight.yangtools.yang.model.util.BitsType;
110 import org.opendaylight.yangtools.yang.model.util.Decimal64;
111 import org.opendaylight.yangtools.yang.model.util.EnumerationType;
112 import org.opendaylight.yangtools.yang.model.util.ExtendedType;
113 import org.opendaylight.yangtools.yang.model.util.InstanceIdentifier;
114 import org.opendaylight.yangtools.yang.model.util.Int16;
115 import org.opendaylight.yangtools.yang.model.util.Int32;
116 import org.opendaylight.yangtools.yang.model.util.Int64;
117 import org.opendaylight.yangtools.yang.model.util.Int8;
118 import org.opendaylight.yangtools.yang.model.util.Leafref;
119 import org.opendaylight.yangtools.yang.model.util.MustDefinitionImpl;
120 import org.opendaylight.yangtools.yang.model.util.RevisionAwareXPathImpl;
121 import org.opendaylight.yangtools.yang.model.util.StringType;
122 import org.opendaylight.yangtools.yang.model.util.Uint16;
123 import org.opendaylight.yangtools.yang.model.util.Uint32;
124 import org.opendaylight.yangtools.yang.model.util.Uint64;
125 import org.opendaylight.yangtools.yang.model.util.Uint8;
126 import org.opendaylight.yangtools.yang.model.util.UnknownType;
127 import org.opendaylight.yangtools.yang.parser.builder.api.Builder;
128 import org.opendaylight.yangtools.yang.parser.builder.api.ConstraintsBuilder;
129 import org.opendaylight.yangtools.yang.parser.builder.api.DataSchemaNodeBuilder;
130 import org.opendaylight.yangtools.yang.parser.builder.api.RefineBuilder;
131 import org.opendaylight.yangtools.yang.parser.builder.api.SchemaNodeBuilder;
132 import org.opendaylight.yangtools.yang.parser.builder.api.TypeDefinitionBuilder;
133 import org.opendaylight.yangtools.yang.parser.builder.impl.ChoiceCaseBuilder;
134 import org.opendaylight.yangtools.yang.parser.builder.impl.RefineHolderImpl;
135 import org.opendaylight.yangtools.yang.parser.builder.impl.UnionTypeBuilder;
136 import org.opendaylight.yangtools.yang.parser.util.TypeConstraints;
137 import org.opendaylight.yangtools.yang.parser.util.UnknownBoundaryNumber;
138 import org.opendaylight.yangtools.yang.parser.util.YangParseException;
139 import org.slf4j.Logger;
140 import org.slf4j.LoggerFactory;
141
142 public final class ParserListenerUtils {
143     private static final Logger LOG = LoggerFactory.getLogger(ParserListenerUtils.class);
144     private static final Splitter KEYDEF_SPLITTER = Splitter.on(' ').omitEmptyStrings();
145     private static final Splitter PIPE_SPLITTER = Splitter.on('|').trimResults();
146     private static final Splitter DOT_DOT_SPLITTER = Splitter.on("..").trimResults();
147     private static final CharMatcher QUOTE_MATCHER = CharMatcher.is('"');
148
149     private ParserListenerUtils() {
150     }
151
152     /**
153      * Parse given tree and get first string value.
154      *
155      * @param treeNode
156      *            tree to parse
157      * @return first string value from given tree
158      */
159     public static String stringFromNode(final ParseTree treeNode) {
160         String result = "";
161         for (int i = 0; i < treeNode.getChildCount(); ++i) {
162             if (treeNode.getChild(i) instanceof StringContext) {
163                 final StringContext context = (StringContext) treeNode.getChild(i);
164                 if (context != null) {
165                     return stringFromStringContext(context);
166                 }
167             }
168         }
169         return result;
170     }
171
172     private static String stringFromStringContext(final StringContext context) {
173         StringBuilder sb = new StringBuilder();
174         for (TerminalNode stringNode : context.STRING()) {
175             final String str = stringNode.getText();
176             final int i = str.indexOf('"');
177
178             if (i == -1) {
179                 sb.append(str);
180                 continue;
181             }
182
183             /*
184              * The string contains quotes, so we have to tread carefully.
185              *
186              * FIXME: I think this code is broken, but proving that requires
187              *        making sense of the parser/lexer and how they tie into
188              *        this method. Especially what format 'str' can have and
189              *        how we need to handle it. The original check was:
190              *
191              *        if (!(result.startsWith("\"")) && result.endsWith("\""))
192              *
193              *        Looking at the parentheses it is hard to justify the
194              *        pair right after negation -- the intent may have been
195              *        to negate the entire expression.
196              */
197             if (i != 0 && str.endsWith("\"")) {
198                 LOG.error("Syntax error in module {} at line {}: missing '\"'.", getParentModule(context),
199                         context.getStart().getLine());
200             } else {
201                 sb.append(QUOTE_MATCHER.removeFrom(str));
202             }
203         }
204         return sb.toString();
205     }
206
207     private static String getParentModule(final ParseTree ctx) {
208         ParseTree current = ctx;
209         while (current != null && !(current instanceof Module_stmtContext)) {
210             current = current.getParent();
211         }
212         if (current != null) {
213             Module_stmtContext module = (Module_stmtContext) current;
214             for (int i = 0; i < module.getChildCount(); i++) {
215                 if (module.getChild(i) instanceof StringContext) {
216                     final StringContext str = (StringContext) module.getChild(i);
217                     return str.getChild(0).getText();
218                 }
219             }
220         }
221         return "";
222     }
223
224     /**
225      * Parse 'description', 'reference' and 'status' statements and fill in
226      * given builder.
227      *
228      * @param ctx
229      *            context to parse
230      * @param builder
231      *            builder to fill in with parsed statements
232      */
233     public static void parseSchemaNodeArgs(final ParseTree ctx, final SchemaNodeBuilder builder) {
234         for (int i = 0; i < ctx.getChildCount(); i++) {
235             final ParseTree child = ctx.getChild(i);
236             if (child instanceof Description_stmtContext) {
237                 final String desc = stringFromNode(child);
238                 builder.setDescription(desc);
239             } else if (child instanceof Reference_stmtContext) {
240                 final String ref = stringFromNode(child);
241                 builder.setReference(ref);
242             } else if (child instanceof Status_stmtContext) {
243                 final Status status = parseStatus((Status_stmtContext) child);
244                 builder.setStatus(status);
245             }
246         }
247     }
248
249     /**
250      * Parse given context and return its value;
251      *
252      * @param ctx
253      *            status context
254      * @return value parsed from context
255      */
256     public static Status parseStatus(final Status_stmtContext ctx) {
257         Status result = null;
258         for (int i = 0; i < ctx.getChildCount(); i++) {
259             ParseTree statusArg = ctx.getChild(i);
260             if (statusArg instanceof Status_argContext) {
261                 String statusArgStr = stringFromNode(statusArg);
262                 switch (statusArgStr) {
263                 case "current":
264                     result = Status.CURRENT;
265                     break;
266                 case "deprecated":
267                     result = Status.DEPRECATED;
268                     break;
269                 case "obsolete":
270                     result = Status.OBSOLETE;
271                     break;
272                 default:
273                     LOG.warn("Invalid 'status' statement: " + statusArgStr);
274                 }
275             }
276         }
277         return result;
278     }
279
280     /**
281      * Parse given tree and returns units statement as string.
282      *
283      * @param ctx
284      *            context to parse
285      * @return value of units statement as string or null if there is no units
286      *         statement
287      */
288     public static String parseUnits(final ParseTree ctx) {
289         String units = null;
290         for (int i = 0; i < ctx.getChildCount(); i++) {
291             ParseTree child = ctx.getChild(i);
292             if (child instanceof Units_stmtContext) {
293                 units = stringFromNode(child);
294                 break;
295             }
296         }
297         return units;
298     }
299
300     /**
301      * Parse given tree and returns default statement as string.
302      *
303      * @param ctx
304      *            context to parse
305      * @return value of default statement as string or null if there is no
306      *         default statement
307      */
308     public static String parseDefault(final ParseTree ctx) {
309         String defaultValue = null;
310         for (int i = 0; i < ctx.getChildCount(); i++) {
311             ParseTree child = ctx.getChild(i);
312             if (child instanceof Default_stmtContext) {
313                 defaultValue = stringFromNode(child);
314                 break;
315             }
316         }
317         return defaultValue;
318     }
319
320     /**
321      * Create SchemaPath from actualPath and new node name.
322      *
323      * @param actualPath
324      *            current position in model
325      * @return SchemaPath object
326      */
327     public static SchemaPath createActualSchemaPath(final Stack<QName> actualPath) {
328         return SchemaPath.create(actualPath, true);
329     }
330
331     /**
332      * Create java.util.List of key node names.
333      *
334      * @param ctx
335      *            Key_stmtContext context
336      * @return YANG list key as java.util.List of key node names
337      */
338     public static List<String> createListKey(final Key_stmtContext ctx) {
339         String keyDefinition = stringFromNode(ctx);
340         return Lists.newArrayList(KEYDEF_SPLITTER.split(keyDefinition));
341     }
342
343     /**
344      * Parse given type body of enumeration statement.
345      *
346      * @param ctx
347      *            type body context to parse
348      * @param path
349      *            actual position in YANG model
350      * @param moduleName
351      *            current module name
352      * @return List of EnumPair object parsed from given context
353      */
354     private static List<EnumTypeDefinition.EnumPair> getEnumConstants(final Type_body_stmtsContext ctx,
355             final Stack<QName> path, final String moduleName) {
356         List<EnumTypeDefinition.EnumPair> enumConstants = new ArrayList<>();
357
358         for (int i = 0; i < ctx.getChildCount(); i++) {
359             ParseTree enumSpecChild = ctx.getChild(i);
360             if (enumSpecChild instanceof Enum_specificationContext) {
361                 int highestValue = -1;
362                 for (int j = 0; j < enumSpecChild.getChildCount(); j++) {
363                     ParseTree enumChild = enumSpecChild.getChild(j);
364                     if (enumChild instanceof Enum_stmtContext) {
365                         EnumPair enumPair = createEnumPair((Enum_stmtContext) enumChild, highestValue, path, moduleName);
366                         if (enumPair.getValue() > highestValue) {
367                             highestValue = enumPair.getValue();
368                         }
369                         enumConstants.add(enumPair);
370                     }
371                 }
372             }
373         }
374         return enumConstants;
375     }
376
377     /**
378      * Parse enum statement context
379      *
380      * @param ctx
381      *            enum statement context
382      * @param highestValue
383      *            current highest value in enumeration
384      * @param actualPath
385      *            actual position in YANG model
386      * @param moduleName
387      *            current module name
388      * @return EnumPair object parsed from given context
389      */
390     private static EnumTypeDefinition.EnumPair createEnumPair(final Enum_stmtContext ctx, final int highestValue,
391             final Stack<QName> actualPath, final String moduleName) {
392         final String name = stringFromNode(ctx);
393         SchemaPath path = createTypePath(actualPath, name);
394         Integer value = null;
395
396         String description = null;
397         String reference = null;
398         Status status = null;
399
400         for (int i = 0; i < ctx.getChildCount(); i++) {
401             ParseTree child = ctx.getChild(i);
402             if (child instanceof Value_stmtContext) {
403                 String valueStr = stringFromNode(child);
404                 value = Integer.valueOf(valueStr);
405             } else if (child instanceof Description_stmtContext) {
406                 description = stringFromNode(child);
407             } else if (child instanceof Reference_stmtContext) {
408                 reference = stringFromNode(child);
409             } else if (child instanceof Status_stmtContext) {
410                 status = parseStatus((Status_stmtContext) child);
411             }
412         }
413
414         if (value == null) {
415             value = highestValue + 1;
416         }
417         if (value < -2147483648 || value > 2147483647) {
418             throw new YangParseException(moduleName, ctx.getStart().getLine(), "Error on enum '" + name
419                     + "': the enum value MUST be in the range from -2147483648 to 2147483647, but was: " + value);
420         }
421
422         EnumPairImpl result = new EnumPairImpl();
423         result.qname = path.getPathTowardsRoot().iterator().next();
424         result.path = path;
425         result.description = description;
426         result.reference = reference;
427         result.status = status;
428         result.name = name;
429         result.value = value;
430         return result;
431     }
432
433     /**
434      * Internal implementation of EnumPair.
435      */
436     private static class EnumPairImpl implements EnumTypeDefinition.EnumPair {
437         private QName qname;
438         private SchemaPath path;
439         private String description;
440         private String reference;
441         private Status status;
442         private final List<UnknownSchemaNode> unknownNodes = Collections.emptyList();
443         private String name;
444         private Integer value;
445
446         @Override
447         public QName getQName() {
448             return qname;
449         }
450
451         @Override
452         public SchemaPath getPath() {
453             return path;
454         }
455
456         @Override
457         public String getDescription() {
458             return description;
459         }
460
461         @Override
462         public String getReference() {
463             return reference;
464         }
465
466         @Override
467         public Status getStatus() {
468             return status;
469         }
470
471         @Override
472         public List<UnknownSchemaNode> getUnknownSchemaNodes() {
473             return unknownNodes;
474         }
475
476         @Override
477         public String getName() {
478             return name;
479         }
480
481         @Override
482         public Integer getValue() {
483             return value;
484         }
485
486         @Override
487         public int hashCode() {
488             final int prime = 31;
489             int result = 1;
490             result = prime * result + ((qname == null) ? 0 : qname.hashCode());
491             result = prime * result + ((path == null) ? 0 : path.hashCode());
492             result = prime * result + ((unknownNodes == null) ? 0 : unknownNodes.hashCode());
493             result = prime * result + ((name == null) ? 0 : name.hashCode());
494             result = prime * result + ((value == null) ? 0 : value.hashCode());
495             return result;
496         }
497
498         @Override
499         public boolean equals(final Object obj) {
500             if (this == obj) {
501                 return true;
502             }
503             if (obj == null) {
504                 return false;
505             }
506             if (getClass() != obj.getClass()) {
507                 return false;
508             }
509             EnumPairImpl other = (EnumPairImpl) obj;
510             if (qname == null) {
511                 if (other.qname != null) {
512                     return false;
513                 }
514             } else if (!qname.equals(other.qname)) {
515                 return false;
516             }
517             if (path == null) {
518                 if (other.path != null) {
519                     return false;
520                 }
521             } else if (!path.equals(other.path)) {
522                 return false;
523             }
524             if (unknownNodes == null) {
525                 if (other.unknownNodes != null) {
526                     return false;
527                 }
528             } else if (!unknownNodes.equals(other.unknownNodes)) {
529                 return false;
530             }
531             if (name == null) {
532                 if (other.name != null) {
533                     return false;
534                 }
535             } else if (!name.equals(other.name)) {
536                 return false;
537             }
538             if (value == null) {
539                 if (other.value != null) {
540                     return false;
541                 }
542             } else if (!value.equals(other.value)) {
543                 return false;
544             }
545             return true;
546         }
547
548         @Override
549         public String toString() {
550             return EnumTypeDefinition.EnumPair.class.getSimpleName() + "[name=" + name + ", value=" + value + "]";
551         }
552     }
553
554     /**
555      * Get and parse range from given type body context.
556      *
557      * @param ctx
558      *            type body context to parse
559      * @param moduleName
560      *            name of current module
561      * @return List of RangeConstraint created from this context
562      */
563     private static List<RangeConstraint> getRangeConstraints(final Type_body_stmtsContext ctx, final String moduleName) {
564         List<RangeConstraint> rangeConstraints = Collections.emptyList();
565         outer: for (int i = 0; i < ctx.getChildCount(); i++) {
566             ParseTree numRestrChild = ctx.getChild(i);
567
568             if (numRestrChild instanceof Numerical_restrictionsContext) {
569                 for (int j = 0; j < numRestrChild.getChildCount(); j++) {
570                     ParseTree rangeChild = numRestrChild.getChild(j);
571                     if (rangeChild instanceof Range_stmtContext) {
572                         rangeConstraints = parseRangeConstraints((Range_stmtContext) rangeChild, moduleName);
573                         break outer;
574                     }
575                 }
576             }
577
578             if (numRestrChild instanceof Decimal64_specificationContext) {
579                 for (int j = 0; j < numRestrChild.getChildCount(); j++) {
580                     ParseTree decRestr = numRestrChild.getChild(j);
581                     if (decRestr instanceof Numerical_restrictionsContext) {
582                         for (int k = 0; k < decRestr.getChildCount(); k++) {
583                             ParseTree rangeChild = decRestr.getChild(k);
584                             if (rangeChild instanceof Range_stmtContext) {
585                                 rangeConstraints = parseRangeConstraints((Range_stmtContext) rangeChild, moduleName);
586                                 break outer;
587                             }
588                         }
589
590                     }
591                 }
592             }
593         }
594         return rangeConstraints;
595     }
596
597     /**
598      * Parse given range context.
599      *
600      * @param ctx
601      *            range context to parse
602      * @param moduleName
603      *            name of current module
604      * @return List of RangeConstraints parsed from this context
605      */
606     private static List<RangeConstraint> parseRangeConstraints(final Range_stmtContext ctx, final String moduleName) {
607         final int line = ctx.getStart().getLine();
608         Optional<String> description = Optional.absent();
609         Optional<String> reference = Optional.absent();
610
611         for (int i = 0; i < ctx.getChildCount(); i++) {
612             ParseTree child = ctx.getChild(i);
613             if (child instanceof Description_stmtContext) {
614                 description = Optional.fromNullable(stringFromNode(child));
615             } else if (child instanceof Reference_stmtContext) {
616                 reference = Optional.fromNullable(stringFromNode(child));
617             }
618         }
619
620         List<RangeConstraint> rangeConstraints = new ArrayList<>();
621         for (String def : PIPE_SPLITTER.split(stringFromNode(ctx))) {
622             final Iterator<String> split = DOT_DOT_SPLITTER.split(def).iterator();
623             final Number min = parseNumberConstraintValue(split.next(), moduleName, line);
624
625             final Number max;
626             if (split.hasNext()) {
627                 max = parseNumberConstraintValue(split.next(), moduleName, line);
628                 if (split.hasNext()) {
629                     throw new YangParseException(moduleName, ctx.getStart().getLine(), "Malformed length constraint \"" + def + "\".");
630                 }
631             } else {
632                 max = min;
633             }
634
635             RangeConstraint range = BaseConstraints.newRangeConstraint(min, max, description, reference);
636             rangeConstraints.add(range);
637         }
638
639         return rangeConstraints;
640     }
641
642     /**
643      * Get and parse length from given type body context.
644      *
645      * @param ctx
646      *            type body context to parse
647      * @param moduleName
648      *            name of current module
649      * @return List of LengthConstraint created from this context
650      */
651     private static List<LengthConstraint> getLengthConstraints(final Type_body_stmtsContext ctx, final String moduleName) {
652         List<LengthConstraint> lengthConstraints = Collections.emptyList();
653         outer: for (int i = 0; i < ctx.getChildCount(); i++) {
654             ParseTree stringRestrChild = ctx.getChild(i);
655             if (stringRestrChild instanceof String_restrictionsContext) {
656                 for (int j = 0; j < stringRestrChild.getChildCount(); j++) {
657                     ParseTree lengthChild = stringRestrChild.getChild(j);
658                     if (lengthChild instanceof Length_stmtContext) {
659                         lengthConstraints = parseLengthConstraints((Length_stmtContext) lengthChild, moduleName);
660                         break outer;
661                     }
662                 }
663             }
664         }
665         return lengthConstraints;
666     }
667
668     /**
669      * Parse given length context.
670      *
671      * @param ctx
672      *            length context to parse
673      * @param moduleName
674      *            name of current module
675      * @return List of LengthConstraints parsed from this context
676      */
677     private static List<LengthConstraint> parseLengthConstraints(final Length_stmtContext ctx, final String moduleName) {
678         final int line = ctx.getStart().getLine();
679         Optional<String> description = Optional.absent();
680         Optional<String> reference = Optional.absent();
681
682         for (int i = 0; i < ctx.getChildCount(); i++) {
683             ParseTree child = ctx.getChild(i);
684             if (child instanceof Description_stmtContext) {
685                 description = Optional.fromNullable(stringFromNode(child));
686             } else if (child instanceof Reference_stmtContext) {
687                 reference = Optional.fromNullable(stringFromNode(child));
688             }
689         }
690
691         List<LengthConstraint> lengthConstraints = new ArrayList<>();
692         for (String def : PIPE_SPLITTER.split(stringFromNode(ctx))) {
693             final Iterator<String> split = DOT_DOT_SPLITTER.split(def).iterator();
694             final Number min = parseNumberConstraintValue(split.next(), moduleName, line);
695
696             final Number max;
697             if (split.hasNext()) {
698                 max = parseNumberConstraintValue(split.next(), moduleName, line);
699                 if (split.hasNext()) {
700                     throw new YangParseException(moduleName, ctx.getStart().getLine(), "Malformed length constraint \"" + def + "\".");
701                 }
702             } else {
703                 max = min;
704             }
705
706             LengthConstraint range = BaseConstraints.newLengthConstraint(min, max, description, reference);
707             lengthConstraints.add(range);
708         }
709
710         return lengthConstraints;
711     }
712
713     /**
714      * @param value
715      *            value to parse
716      * @param moduleName
717      *            name of current module
718      * @param line
719      *            current line in module
720      * @return wrapper object of primitive java type or UnknownBoundaryNumber if
721      *         type is one of special YANG values 'min' or 'max'
722      */
723     private static Number parseNumberConstraintValue(final String value, final String moduleName, final int line) {
724         Number result;
725         if ("min".equals(value) || "max".equals(value)) {
726             result = new UnknownBoundaryNumber(value);
727         } else {
728             try {
729                 if (value.indexOf('.') != -1) {
730                     result = new BigDecimal(value);
731                 } else {
732                     result = new BigInteger(value);
733                 }
734             } catch (NumberFormatException e) {
735                 throw new YangParseException(moduleName, line, "Unable to parse range value '" + value + "'.", e);
736             }
737         }
738         return result;
739     }
740
741     /**
742      * Parse type body and return pattern constraints.
743      *
744      * @param ctx
745      *            type body
746      * @return list of pattern constraints
747      */
748     private static List<PatternConstraint> getPatternConstraint(final Type_body_stmtsContext ctx) {
749         List<PatternConstraint> patterns = new ArrayList<>();
750
751         for (int i = 0; i < ctx.getChildCount(); i++) {
752             ParseTree stringRestrChild = ctx.getChild(i);
753             if (stringRestrChild instanceof String_restrictionsContext) {
754                 for (int j = 0; j < stringRestrChild.getChildCount(); j++) {
755                     ParseTree lengthChild = stringRestrChild.getChild(j);
756                     if (lengthChild instanceof Pattern_stmtContext) {
757                         patterns.add(parsePatternConstraint((Pattern_stmtContext) lengthChild));
758                     }
759                 }
760             }
761         }
762         return patterns;
763     }
764
765     /**
766      * Internal helper method.
767      *
768      * @param ctx
769      *            pattern context
770      * @return PatternConstraint object
771      */
772     private static PatternConstraint parsePatternConstraint(final Pattern_stmtContext ctx) {
773         String description = null;
774         String reference = null;
775         for (int i = 0; i < ctx.getChildCount(); i++) {
776             ParseTree child = ctx.getChild(i);
777             if (child instanceof Description_stmtContext) {
778                 description = stringFromNode(child);
779             } else if (child instanceof Reference_stmtContext) {
780                 reference = stringFromNode(child);
781             }
782         }
783         String pattern = parsePatternString(ctx);
784         return BaseConstraints.patternConstraint(pattern, description, reference);
785     }
786
787     /**
788      * Parse given context and return pattern value.
789      *
790      * @param ctx
791      *            context to parse
792      * @return pattern value as String
793      */
794     private static String parsePatternString(final Pattern_stmtContext ctx) {
795         StringBuilder result = new StringBuilder();
796         for (int i = 0; i < ctx.getChildCount(); ++i) {
797             ParseTree child = ctx.getChild(i);
798             if (child instanceof StringContext) {
799                 for (int j = 0; j < child.getChildCount(); j++) {
800                     if (j % 2 == 0) {
801                         String patternToken = child.getChild(j).getText();
802                         result.append(patternToken.substring(1, patternToken.length() - 1));
803                     }
804                 }
805             }
806         }
807         return result.toString();
808     }
809
810     /**
811      * Get fraction digits value from type body.
812      *
813      * @param ctx
814      *            type body context to parse
815      * @param moduleName
816      *            name of current module
817      * @return 'fraction-digits' value if present in given context, null
818      *         otherwise
819      */
820     private static Integer getFractionDigits(final Type_body_stmtsContext ctx, final String moduleName) {
821         Integer result = null;
822         for (int i = 0; i < ctx.getChildCount(); i++) {
823             ParseTree dec64specChild = ctx.getChild(i);
824             if (dec64specChild instanceof Decimal64_specificationContext) {
825                 result = parseFractionDigits((Decimal64_specificationContext) dec64specChild, moduleName);
826             }
827         }
828         return result;
829     }
830
831     /**
832      * Parse decimal64 fraction-digits value.
833      *
834      * @param ctx
835      *            decimal64 context
836      * @param moduleName
837      *            name of current module
838      * @return fraction-digits value as Integer
839      */
840     private static Integer parseFractionDigits(final Decimal64_specificationContext ctx, final String moduleName) {
841         Integer result = null;
842         for (int i = 0; i < ctx.getChildCount(); i++) {
843             ParseTree fdChild = ctx.getChild(i);
844             if (fdChild instanceof Fraction_digits_stmtContext) {
845                 String value = stringFromNode(fdChild);
846                 try {
847                     result = Integer.valueOf(value);
848                 } catch (NumberFormatException e) {
849                     throw new YangParseException(moduleName, ctx.getStart().getLine(),
850                             "Unable to parse fraction digits value '" + value + "'.", e);
851                 }
852             }
853         }
854         return result;
855     }
856
857     /**
858      * Internal helper method for parsing bit statements from given type body
859      * context.
860      *
861      * @param ctx
862      *            type body context to parse
863      * @param actualPath
864      *            current position in YANG model
865      * @param moduleName
866      *            current module name
867      * @return List of Bit objects created from this context
868      */
869     private static List<BitsTypeDefinition.Bit> getBits(final Type_body_stmtsContext ctx, final Stack<QName> actualPath,
870             final String moduleName) {
871         final List<BitsTypeDefinition.Bit> bits = new ArrayList<>();
872         for (int j = 0; j < ctx.getChildCount(); j++) {
873             ParseTree bitsSpecChild = ctx.getChild(j);
874             if (bitsSpecChild instanceof Bits_specificationContext) {
875                 long highestPosition = -1;
876                 for (int k = 0; k < bitsSpecChild.getChildCount(); k++) {
877                     ParseTree bitChild = bitsSpecChild.getChild(k);
878                     if (bitChild instanceof Bit_stmtContext) {
879                         Bit bit = parseBit((Bit_stmtContext) bitChild, highestPosition, actualPath, moduleName);
880                         if (bit.getPosition() > highestPosition) {
881                             highestPosition = bit.getPosition();
882                         }
883                         bits.add(bit);
884                     }
885                 }
886             }
887         }
888         return bits;
889     }
890
891     /**
892      * Internal helper method for parsing bit context.
893      *
894      * @param ctx
895      *            bit statement context to parse
896      * @param highestPosition
897      *            current highest position in bits type
898      * @param actualPath
899      *            current position in YANG model
900      * @param moduleName
901      *            current module name
902      * @return Bit object parsed from this context
903      */
904     private static BitsTypeDefinition.Bit parseBit(final Bit_stmtContext ctx, final long highestPosition,
905             final Stack<QName> actualPath, final String moduleName) {
906         String name = stringFromNode(ctx);
907         Long position = null;
908
909         String description = null;
910         String reference = null;
911         Status status = Status.CURRENT;
912
913         SchemaPath schemaPath = createBaseTypePath(actualPath, name);
914
915         for (int i = 0; i < ctx.getChildCount(); i++) {
916             ParseTree child = ctx.getChild(i);
917             if (child instanceof Position_stmtContext) {
918                 String positionStr = stringFromNode(child);
919                 position = Long.valueOf(positionStr);
920             } else if (child instanceof Description_stmtContext) {
921                 description = stringFromNode(child);
922             } else if (child instanceof Reference_stmtContext) {
923                 reference = stringFromNode(child);
924             } else if (child instanceof Status_stmtContext) {
925                 status = parseStatus((Status_stmtContext) child);
926             }
927         }
928
929         if (position == null) {
930             position = highestPosition + 1;
931         }
932         if (position < 0 || position > 4294967295L) {
933             throw new YangParseException(moduleName, ctx.getStart().getLine(), "Error on bit '" + name
934                     + "': the position value MUST be in the range 0 to 4294967295");
935         }
936
937         final List<UnknownSchemaNode> unknownNodes = Collections.emptyList();
938         return new BitImpl(position, schemaPath.getPathTowardsRoot().iterator().next(), schemaPath,
939                 description, reference, status, unknownNodes);
940     }
941
942     /**
943      * Parse 'ordered-by' statement.
944      *
945      * The 'ordered-by' statement defines whether the order of entries within a
946      * list are determined by the user or the system. The argument is one of the
947      * strings "system" or "user". If not present, order defaults to "system".
948      *
949      * @param ctx
950      *            Ordered_by_stmtContext
951      * @return true, if ordered-by contains value 'user', false otherwise
952      */
953     public static boolean parseUserOrdered(final Ordered_by_stmtContext ctx) {
954         boolean result = false;
955         for (int j = 0; j < ctx.getChildCount(); j++) {
956             ParseTree orderArg = ctx.getChild(j);
957             if (orderArg instanceof Ordered_by_argContext) {
958                 String orderStr = stringFromNode(orderArg);
959                 switch (orderStr) {
960                 case "system":
961                     result = false;
962                     break;
963                 case "user":
964                     result = true;
965                     break;
966                 default:
967                     LOG.warn("Invalid 'ordered-by' statement.");
968                 }
969             }
970         }
971         return result;
972     }
973
974     /**
975      * Get config statement from given context. If there is no config statement,
976      * return config value of parent
977      *
978      * @param ctx
979      *            context to parse
980      * @param node
981      *            current node
982      * @param moduleName
983      *            name of current module
984      * @param line
985      *            line in current module
986      * @return config statement parsed from given context
987      */
988     public static boolean getConfig(final ParseTree ctx, final Builder node, final String moduleName, final int line) {
989         boolean result;
990         // parse configuration statement
991         Boolean config = null;
992         for (int i = 0; i < ctx.getChildCount(); i++) {
993             ParseTree child = ctx.getChild(i);
994             if (child instanceof Config_stmtContext) {
995                 config = parseConfig((Config_stmtContext) child, moduleName);
996                 break;
997             }
998         }
999
1000         // If 'config' is not specified, the default is the same as the parent
1001         // schema node's 'config' value
1002         boolean parentConfig = getParentConfig(node);
1003         if (config == null) {
1004             result = parentConfig;
1005         } else {
1006             // Check: if a node has 'config' set to 'false', no node underneath
1007             // it can have 'config' set to 'true'
1008             if (!parentConfig && config) {
1009                 throw new YangParseException(moduleName, line,
1010                         "Can not set 'config' to 'true' if parent node has 'config' set to 'false'");
1011             }
1012             result = config;
1013         }
1014
1015         return result;
1016     }
1017
1018     private static boolean getParentConfig(final Builder node) {
1019         Builder parent = node.getParent();
1020         boolean config;
1021
1022         if (parent instanceof ChoiceCaseBuilder) {
1023             parent = parent.getParent();
1024         }
1025         if (parent instanceof DataSchemaNodeBuilder) {
1026             config = ((DataSchemaNodeBuilder) parent).isConfiguration();
1027         } else {
1028             config = true;
1029         }
1030         return config;
1031     }
1032
1033     /**
1034      * Parse config statement.
1035      *
1036      * @param ctx
1037      *            config context to parse
1038      * @param moduleName
1039      *            current module name
1040      * @return true if given context contains string 'true', false otherwise
1041      */
1042     private static Boolean parseConfig(final Config_stmtContext ctx, final String moduleName) {
1043         Boolean result = null;
1044         if (ctx != null) {
1045             for (int i = 0; i < ctx.getChildCount(); ++i) {
1046                 final ParseTree configContext = ctx.getChild(i);
1047                 if (configContext instanceof Config_argContext) {
1048                     final String value = stringFromNode(configContext);
1049                     switch (value) {
1050                     case "true":
1051                         result = true;
1052                         break;
1053                     case "false":
1054                         result = false;
1055                         break;
1056                     default:
1057                         throw new YangParseException(moduleName, ctx.getStart().getLine(),
1058                                 "Failed to parse 'config' statement value: '" + value + "'.");
1059                     }
1060                 }
1061             }
1062         }
1063         return result;
1064     }
1065
1066     /**
1067      * Parse type body and create UnknownType definition.
1068      *
1069      * @param typedefQName
1070      *            qname of current type
1071      * @param ctx
1072      *            type body
1073      * @param actualPath
1074      *            actual path in model
1075      * @param namespace
1076      *            module namespace
1077      * @param revision
1078      *            module revision
1079      * @param prefix
1080      *            module prefix
1081      * @param parent
1082      *            current node parent
1083      * @return UnknownType object with constraints from parsed type body
1084      */
1085     public static TypeDefinition<?> parseUnknownTypeWithBody(final QName typedefQName,
1086             final Type_body_stmtsContext ctx, final Stack<QName> actualPath, final URI namespace, final Date revision,
1087             final String prefix, final Builder parent) {
1088         String moduleName = parent.getModuleName();
1089         String typeName = typedefQName.getLocalName();
1090
1091         UnknownType.Builder unknownType = new UnknownType.Builder(typedefQName);
1092
1093         if (ctx != null) {
1094             List<RangeConstraint> rangeStatements = getRangeConstraints(ctx, moduleName);
1095             List<LengthConstraint> lengthStatements = getLengthConstraints(ctx, moduleName);
1096             List<PatternConstraint> patternStatements = getPatternConstraint(ctx);
1097             Integer fractionDigits = getFractionDigits(ctx, moduleName);
1098
1099             if (parent instanceof TypeDefinitionBuilder) {
1100                 TypeDefinitionBuilder typedef = (TypeDefinitionBuilder) parent;
1101                 typedef.setRanges(rangeStatements);
1102                 typedef.setLengths(lengthStatements);
1103                 typedef.setPatterns(patternStatements);
1104                 typedef.setFractionDigits(fractionDigits);
1105                 return unknownType.build();
1106             } else {
1107                 TypeDefinition<?> baseType = unknownType.build();
1108                 QName qname = new QName(namespace, revision, prefix, typeName);
1109                 SchemaPath schemaPath = createTypePath(actualPath, typeName);
1110
1111                 ExtendedType.Builder typeBuilder = new ExtendedType.Builder(qname, baseType, null, null, schemaPath);
1112                 typeBuilder.ranges(rangeStatements);
1113                 typeBuilder.lengths(lengthStatements);
1114                 typeBuilder.patterns(patternStatements);
1115                 typeBuilder.fractionDigits(fractionDigits);
1116
1117                 return typeBuilder.build();
1118             }
1119         }
1120
1121         return unknownType.build();
1122     }
1123
1124     /**
1125      * Create TypeDefinition object based on given type name and type body.
1126      *
1127      * @param typeName
1128      *            name of type
1129      * @param typeBody
1130      *            type body context
1131      * @param actualPath
1132      *            current path in schema
1133      * @param namespace
1134      *            current namespace
1135      * @param revision
1136      *            current revision
1137      * @param prefix
1138      *            current prefix
1139      * @param parent
1140      *            parent builder
1141      * @return TypeDefinition object based on parsed values.
1142      */
1143     public static TypeDefinition<?> parseTypeWithBody(final String typeName, final Type_body_stmtsContext typeBody,
1144             final Stack<QName> actualPath, final URI namespace, final Date revision, final String prefix,
1145             final Builder parent) {
1146
1147         final String moduleName = parent.getModuleName();
1148         final int line = typeBody.getStart().getLine();
1149         TypeDefinition<?> baseType = null;
1150
1151         Integer fractionDigits = getFractionDigits(typeBody, moduleName);
1152         List<LengthConstraint> lengthStatements = getLengthConstraints(typeBody, moduleName);
1153         List<PatternConstraint> patternStatements = getPatternConstraint(typeBody);
1154         List<RangeConstraint> rangeStatements = getRangeConstraints(typeBody, moduleName);
1155
1156         TypeConstraints constraints = new TypeConstraints(moduleName, line);
1157         constraints.addFractionDigits(fractionDigits);
1158         constraints.addLengths(lengthStatements);
1159         constraints.addPatterns(patternStatements);
1160         constraints.addRanges(rangeStatements);
1161
1162         SchemaPath baseTypePath = createBaseTypePath(actualPath, typeName);
1163         SchemaPath extBaseTypePath = createExtendedBaseTypePath(actualPath, namespace, revision, prefix, typeName);
1164
1165         if (parent instanceof TypeDefinitionBuilder && !(parent instanceof UnionTypeBuilder)) {
1166             extBaseTypePath = baseTypePath;
1167         }
1168
1169         if ("decimal64".equals(typeName)) {
1170             if (rangeStatements.isEmpty()) {
1171                 try {
1172                     return new Decimal64(baseTypePath, fractionDigits);
1173                 } catch(Exception e) {
1174                     throw new YangParseException(moduleName, line, e.getMessage());
1175                 }
1176             }
1177             Decimal64 decimalType = new Decimal64(extBaseTypePath, fractionDigits);
1178             constraints.addRanges(decimalType.getRangeConstraints());
1179             baseType = decimalType;
1180         } else if (typeName.startsWith("int")) {
1181             IntegerTypeDefinition intType = null;
1182             switch (typeName) {
1183             case "int8":
1184                 intType = Int8.getInstance();
1185                 break;
1186             case "int16":
1187                 intType = Int16.getInstance();
1188                 break;
1189             case "int32":
1190                 intType = Int32.getInstance();
1191                 break;
1192             case "int64":
1193                 intType = Int64.getInstance();
1194                 break;
1195             }
1196             if (intType == null) {
1197                 throw new YangParseException(moduleName, line, "Unknown yang type " + typeName);
1198             }
1199             constraints.addRanges(intType.getRangeConstraints());
1200             baseType = intType;
1201         } else if (typeName.startsWith("uint")) {
1202             UnsignedIntegerTypeDefinition uintType = null;
1203             switch (typeName) {
1204             case "uint8":
1205                 uintType = Uint8.getInstance();
1206                 break;
1207             case "uint16":
1208                 uintType = Uint16.getInstance();
1209                 break;
1210             case "uint32":
1211                 uintType = Uint32.getInstance();
1212                 break;
1213             case "uint64":
1214                 uintType = Uint64.getInstance();
1215                 break;
1216             }
1217             if (uintType == null) {
1218                 throw new YangParseException(moduleName, line, "Unknown yang type " + typeName);
1219             }
1220             constraints.addRanges(uintType.getRangeConstraints());
1221             baseType = uintType;
1222         } else if ("enumeration".equals(typeName)) {
1223             List<EnumTypeDefinition.EnumPair> enumConstants = getEnumConstants(typeBody, actualPath, moduleName);
1224             return EnumerationType.create(baseTypePath, enumConstants, Optional.<EnumPair> absent());
1225         } else if ("string".equals(typeName)) {
1226             StringTypeDefinition stringType = StringType.getInstance();
1227             constraints.addLengths(stringType.getLengthConstraints());
1228             baseType = stringType;
1229         } else if ("bits".equals(typeName)) {
1230             return BitsType.create(baseTypePath, getBits(typeBody, actualPath, moduleName));
1231         } else if ("leafref".equals(typeName)) {
1232             final String path = parseLeafrefPath(typeBody);
1233             final boolean absolute = path.startsWith("/");
1234             RevisionAwareXPath xpath = new RevisionAwareXPathImpl(path, absolute);
1235             return new Leafref(xpath);
1236         } else if ("binary".equals(typeName)) {
1237             BinaryTypeDefinition binaryType = BinaryType.getInstance();
1238             constraints.addLengths(binaryType.getLengthConstraints());
1239             baseType = binaryType;
1240         } else if ("instance-identifier".equals(typeName)) {
1241             return InstanceIdentifier.create(isRequireInstance(typeBody));
1242         }
1243
1244         if (parent instanceof TypeDefinitionBuilder && !(parent instanceof UnionTypeBuilder)) {
1245             TypeDefinitionBuilder typedef = (TypeDefinitionBuilder) parent;
1246             typedef.setRanges(constraints.getRange());
1247             typedef.setLengths(constraints.getLength());
1248             typedef.setPatterns(constraints.getPatterns());
1249             typedef.setFractionDigits(constraints.getFractionDigits());
1250             return baseType;
1251         }
1252
1253         List<QName> path = new ArrayList<>(actualPath);
1254         path.add(new QName(namespace, revision, prefix, typeName));
1255         SchemaPath schemaPath = SchemaPath.create(path, true);
1256
1257         QName qname = schemaPath.getPath().get(schemaPath.getPath().size() - 1);
1258         ExtendedType.Builder typeBuilder = new ExtendedType.Builder(qname, baseType, "", "", schemaPath);
1259
1260         typeBuilder.ranges(constraints.getRange());
1261         typeBuilder.lengths(constraints.getLength());
1262         typeBuilder.patterns(constraints.getPatterns());
1263         typeBuilder.fractionDigits(constraints.getFractionDigits());
1264
1265         return typeBuilder.build();
1266     }
1267
1268     private static SchemaPath createTypePath(final Stack<QName> actual, final String typeName) {
1269         QName last = actual.peek();
1270         QName typeQName = new QName(last.getNamespace(), last.getRevision(), last.getPrefix(), typeName);
1271         List<QName> path = new ArrayList<>(actual);
1272         path.add(typeQName);
1273         return SchemaPath.create(path, true);
1274     }
1275
1276     private static SchemaPath createBaseTypePath(final Stack<QName> actual, final String typeName) {
1277         List<QName> path = new ArrayList<>(actual);
1278         path.add(BaseTypes.constructQName(typeName));
1279         return SchemaPath.create(path, true);
1280     }
1281
1282     private static SchemaPath createExtendedBaseTypePath(final Stack<QName> actual, final URI namespace, final Date revision,
1283             final String prefix, final String typeName) {
1284         QName extTypeName = new QName(namespace, revision, prefix, typeName);
1285         QName baseTypeName = BaseTypes.constructQName(typeName);
1286         List<QName> path = new ArrayList<>(actual);
1287         path.add(extTypeName);
1288         path.add(baseTypeName);
1289         return SchemaPath.create(path, true);
1290     }
1291
1292     /**
1293      * Parse given context and find identityref base value.
1294      *
1295      * @param ctx
1296      *            type body
1297      * @return identityref base value as String
1298      */
1299     public static String getIdentityrefBase(final Type_body_stmtsContext ctx) {
1300         String result = null;
1301         outer: for (int i = 0; i < ctx.getChildCount(); i++) {
1302             ParseTree child = ctx.getChild(i);
1303             if (child instanceof Identityref_specificationContext) {
1304                 for (int j = 0; j < child.getChildCount(); j++) {
1305                     ParseTree baseArg = child.getChild(j);
1306                     if (baseArg instanceof Base_stmtContext) {
1307                         result = stringFromNode(baseArg);
1308                         break outer;
1309                     }
1310                 }
1311             }
1312         }
1313         return result;
1314     }
1315
1316     /**
1317      * Parse type body statement and find require-instance value.
1318      *
1319      * @param ctx
1320      *            type body context
1321      * @return require-instance value
1322      */
1323     private static boolean isRequireInstance(final Type_body_stmtsContext ctx) {
1324         for (int i = 0; i < ctx.getChildCount(); i++) {
1325             ParseTree child = ctx.getChild(i);
1326             if (child instanceof Instance_identifier_specificationContext) {
1327                 for (int j = 0; j < child.getChildCount(); j++) {
1328                     ParseTree reqStmt = child.getChild(j);
1329                     if (reqStmt instanceof Require_instance_stmtContext) {
1330                         for (int k = 0; k < reqStmt.getChildCount(); k++) {
1331                             ParseTree reqArg = reqStmt.getChild(k);
1332                             if (reqArg instanceof Require_instance_argContext) {
1333                                 return Boolean.valueOf(stringFromNode(reqArg));
1334                             }
1335                         }
1336                     }
1337                 }
1338             }
1339         }
1340         return true;
1341     }
1342
1343     /**
1344      * Parse type body statement and find leafref path.
1345      *
1346      * @param ctx
1347      *            type body context
1348      * @return leafref path as String
1349      */
1350     private static String parseLeafrefPath(final Type_body_stmtsContext ctx) {
1351         for (int i = 0; i < ctx.getChildCount(); i++) {
1352             ParseTree child = ctx.getChild(i);
1353             if (child instanceof Leafref_specificationContext) {
1354                 for (int j = 0; j < child.getChildCount(); j++) {
1355                     ParseTree leafRefSpec = child.getChild(j);
1356                     if (leafRefSpec instanceof Path_stmtContext) {
1357                         return stringFromNode(leafRefSpec);
1358                     }
1359                 }
1360             }
1361         }
1362         return null;
1363     }
1364
1365     /**
1366      * Internal helper method for parsing must statement.
1367      *
1368      * @param ctx
1369      *            Must_stmtContext
1370      * @return MustDefinition object based on parsed context
1371      */
1372     private static MustDefinition parseMust(final YangParser.Must_stmtContext ctx) {
1373         StringBuilder mustText = new StringBuilder();
1374         String description = null;
1375         String reference = null;
1376         String errorAppTag = null;
1377         String errorMessage = null;
1378         for (int i = 0; i < ctx.getChildCount(); ++i) {
1379             ParseTree child = ctx.getChild(i);
1380             if (child instanceof StringContext) {
1381                 final StringContext context = (StringContext) child;
1382                 if (context.getChildCount() == 1) {
1383                     String mustPart = context.getChild(0).getText();
1384                     // trim start and end quotation
1385                     mustText.append(mustPart.substring(1, mustPart.length() - 1));
1386                 } else {
1387                     for (int j = 0; j < context.getChildCount(); j++) {
1388                         String mustPart = context.getChild(j).getText();
1389                         if (j == 0) {
1390                             mustText.append(mustPart.substring(0, mustPart.length() - 1));
1391                             continue;
1392                         }
1393                         if (j % 2 == 0) {
1394                             mustText.append(mustPart.substring(1));
1395                         }
1396                     }
1397                 }
1398             } else if (child instanceof Description_stmtContext) {
1399                 description = stringFromNode(child);
1400             } else if (child instanceof Reference_stmtContext) {
1401                 reference = stringFromNode(child);
1402             } else if (child instanceof Error_app_tag_stmtContext) {
1403                 errorAppTag = stringFromNode(child);
1404             } else if (child instanceof Error_message_stmtContext) {
1405                 errorMessage = stringFromNode(child);
1406             }
1407         }
1408
1409         return new MustDefinitionImpl(mustText.toString(), description, reference, errorAppTag, errorMessage);
1410     }
1411
1412     /**
1413      * Parse given context and set constraints to constraints builder.
1414      *
1415      * @param ctx
1416      *            context to parse
1417      * @param constraints
1418      *            ConstraintsBuilder to fill
1419      */
1420     public static void parseConstraints(final ParseTree ctx, final ConstraintsBuilder constraints) {
1421         for (int i = 0; i < ctx.getChildCount(); ++i) {
1422             final ParseTree childNode = ctx.getChild(i);
1423             if (childNode instanceof Max_elements_stmtContext) {
1424                 Integer max = parseMaxElements((Max_elements_stmtContext) childNode, constraints.getModuleName());
1425                 constraints.setMaxElements(max);
1426             } else if (childNode instanceof Min_elements_stmtContext) {
1427                 Integer min = parseMinElements((Min_elements_stmtContext) childNode, constraints.getModuleName());
1428                 constraints.setMinElements(min);
1429             } else if (childNode instanceof Must_stmtContext) {
1430                 MustDefinition must = parseMust((Must_stmtContext) childNode);
1431                 constraints.addMustDefinition(must);
1432             } else if (childNode instanceof Mandatory_stmtContext) {
1433                 for (int j = 0; j < childNode.getChildCount(); j++) {
1434                     ParseTree mandatoryTree = childNode.getChild(j);
1435                     if (mandatoryTree instanceof Mandatory_argContext) {
1436                         Boolean mandatory = Boolean.valueOf(stringFromNode(mandatoryTree));
1437                         constraints.setMandatory(mandatory);
1438                     }
1439                 }
1440             } else if (childNode instanceof When_stmtContext) {
1441                 constraints.addWhenCondition(stringFromNode(childNode));
1442             }
1443         }
1444     }
1445
1446     private static Integer parseMinElements(final Min_elements_stmtContext ctx, final String moduleName) {
1447         Integer result = null;
1448         try {
1449             for (int i = 0; i < ctx.getChildCount(); i++) {
1450                 ParseTree minArg = ctx.getChild(i);
1451                 if (minArg instanceof Min_value_argContext) {
1452                     result = Integer.valueOf(stringFromNode(minArg));
1453                 }
1454             }
1455             if (result == null) {
1456                 throw new IllegalArgumentException();
1457             }
1458             return result;
1459         } catch (Exception e) {
1460             throw new YangParseException(moduleName, ctx.getStart().getLine(), "Failed to parse min-elements.", e);
1461         }
1462     }
1463
1464     private static Integer parseMaxElements(final Max_elements_stmtContext ctx, final String moduleName) {
1465         Integer result = null;
1466         try {
1467             for (int i = 0; i < ctx.getChildCount(); i++) {
1468                 ParseTree maxArg = ctx.getChild(i);
1469                 if (maxArg instanceof Max_value_argContext) {
1470                     result = Integer.valueOf(stringFromNode(maxArg));
1471                 }
1472             }
1473             if (result == null) {
1474                 throw new IllegalArgumentException();
1475             }
1476             return result;
1477         } catch (Exception e) {
1478             throw new YangParseException(moduleName, ctx.getStart().getLine(), "Failed to parse max-elements.", e);
1479         }
1480     }
1481
1482     /**
1483      * Parse given context and return yin value.
1484      *
1485      * @param ctx
1486      *            context to parse
1487      * @return true if value is 'true', false otherwise
1488      */
1489     public static boolean parseYinValue(final Argument_stmtContext ctx) {
1490         boolean yinValue = false;
1491         outer: for (int i = 0; i < ctx.getChildCount(); i++) {
1492             ParseTree yin = ctx.getChild(i);
1493             if (yin instanceof Yin_element_stmtContext) {
1494                 for (int j = 0; j < yin.getChildCount(); j++) {
1495                     ParseTree yinArg = yin.getChild(j);
1496                     if (yinArg instanceof Yin_element_argContext) {
1497                         String yinString = stringFromNode(yinArg);
1498                         if ("true".equals(yinString)) {
1499                             yinValue = true;
1500                             break outer;
1501                         }
1502                     }
1503                 }
1504             }
1505         }
1506         return yinValue;
1507     }
1508
1509     /**
1510      * Check this base type.
1511      *
1512      * @param typeName
1513      *            base YANG type name
1514      * @param moduleName
1515      *            name of current module
1516      * @param line
1517      *            line in module
1518      * @throws YangParseException
1519      *             if this is one of YANG type which MUST contain additional
1520      *             informations in its body
1521      */
1522     public static void checkMissingBody(final String typeName, final String moduleName, final int line) {
1523         switch (typeName) {
1524         case "decimal64":
1525             throw new YangParseException(moduleName, line,
1526                     "The 'fraction-digits' statement MUST be present if the type is 'decimal64'.");
1527         case "identityref":
1528             throw new YangParseException(moduleName, line,
1529                     "The 'base' statement MUST be present if the type is 'identityref'.");
1530         case "leafref":
1531             throw new YangParseException(moduleName, line,
1532                     "The 'path' statement MUST be present if the type is 'leafref'.");
1533         case "bits":
1534             throw new YangParseException(moduleName, line, "The 'bit' statement MUST be present if the type is 'bits'.");
1535         case "enumeration":
1536             throw new YangParseException(moduleName, line,
1537                     "The 'enum' statement MUST be present if the type is 'enumeration'.");
1538         }
1539     }
1540
1541     /**
1542      * Parse refine statement.
1543      *
1544      * @param refineCtx
1545      *            refine statement
1546      * @param moduleName
1547      *            name of current module
1548      * @return RefineHolder object representing this refine statement
1549      */
1550     public static RefineHolderImpl parseRefine(final Refine_stmtContext refineCtx, final String moduleName) {
1551         final String refineTarget = stringFromNode(refineCtx);
1552         final RefineHolderImpl refine = new RefineHolderImpl(moduleName, refineCtx.getStart().getLine(), refineTarget);
1553         for (int i = 0; i < refineCtx.getChildCount(); i++) {
1554             ParseTree refinePom = refineCtx.getChild(i);
1555             if (refinePom instanceof Refine_pomContext) {
1556                 for (int j = 0; j < refinePom.getChildCount(); j++) {
1557                     ParseTree refineStmt = refinePom.getChild(j);
1558                     parseRefineDefault(refine, refineStmt);
1559
1560                     if (refineStmt instanceof Refine_leaf_stmtsContext) {
1561                         parseRefine(refine, (Refine_leaf_stmtsContext) refineStmt);
1562                     } else if (refineStmt instanceof Refine_container_stmtsContext) {
1563                         parseRefine(refine, (Refine_container_stmtsContext) refineStmt);
1564                     } else if (refineStmt instanceof Refine_list_stmtsContext) {
1565                         parseRefine(refine, (Refine_list_stmtsContext) refineStmt);
1566                     } else if (refineStmt instanceof Refine_leaf_list_stmtsContext) {
1567                         parseRefine(refine, (Refine_leaf_list_stmtsContext) refineStmt);
1568                     } else if (refineStmt instanceof Refine_choice_stmtsContext) {
1569                         parseRefine(refine, (Refine_choice_stmtsContext) refineStmt);
1570                     } else if (refineStmt instanceof Refine_anyxml_stmtsContext) {
1571                         parseRefine(refine, (Refine_anyxml_stmtsContext) refineStmt);
1572                     }
1573                 }
1574             }
1575         }
1576         return refine;
1577     }
1578
1579     private static void parseRefineDefault(final RefineHolderImpl refine, final ParseTree refineStmt) {
1580         for (int i = 0; i < refineStmt.getChildCount(); i++) {
1581             ParseTree refineArg = refineStmt.getChild(i);
1582             if (refineArg instanceof Description_stmtContext) {
1583                 String description = stringFromNode(refineArg);
1584                 refine.setDescription(description);
1585             } else if (refineArg instanceof Reference_stmtContext) {
1586                 String reference = stringFromNode(refineArg);
1587                 refine.setReference(reference);
1588             } else if (refineArg instanceof Config_stmtContext) {
1589                 Boolean config = parseConfig((Config_stmtContext) refineArg, refine.getModuleName());
1590                 refine.setConfiguration(config);
1591             }
1592         }
1593     }
1594
1595     private static RefineBuilder parseRefine(final RefineHolderImpl refine, final Refine_leaf_stmtsContext refineStmt) {
1596         for (int i = 0; i < refineStmt.getChildCount(); i++) {
1597             ParseTree refineArg = refineStmt.getChild(i);
1598             if (refineArg instanceof Default_stmtContext) {
1599                 String defaultStr = stringFromNode(refineArg);
1600                 refine.setDefaultStr(defaultStr);
1601             } else if (refineArg instanceof Mandatory_stmtContext) {
1602                 for (int j = 0; j < refineArg.getChildCount(); j++) {
1603                     ParseTree mandatoryTree = refineArg.getChild(j);
1604                     if (mandatoryTree instanceof Mandatory_argContext) {
1605                         Boolean mandatory = Boolean.valueOf(stringFromNode(mandatoryTree));
1606                         refine.setMandatory(mandatory);
1607                     }
1608                 }
1609             } else if (refineArg instanceof Must_stmtContext) {
1610                 MustDefinition must = parseMust((Must_stmtContext) refineArg);
1611                 refine.setMust(must);
1612
1613             }
1614         }
1615         return refine;
1616     }
1617
1618     private static RefineBuilder parseRefine(final RefineBuilder refine, final Refine_container_stmtsContext refineStmt) {
1619         for (int i = 0; i < refineStmt.getChildCount(); i++) {
1620             ParseTree refineArg = refineStmt.getChild(i);
1621             if (refineArg instanceof Must_stmtContext) {
1622                 MustDefinition must = parseMust((Must_stmtContext) refineArg);
1623                 refine.setMust(must);
1624             } else if (refineArg instanceof Presence_stmtContext) {
1625                 refine.setPresence(true);
1626             }
1627         }
1628         return refine;
1629     }
1630
1631     private static RefineBuilder parseRefine(final RefineHolderImpl refine, final Refine_list_stmtsContext refineStmt) {
1632         for (int i = 0; i < refineStmt.getChildCount(); i++) {
1633             ParseTree refineArg = refineStmt.getChild(i);
1634             if (refineArg instanceof Must_stmtContext) {
1635                 MustDefinition must = parseMust((Must_stmtContext) refineArg);
1636                 refine.setMust(must);
1637             } else if (refineArg instanceof Max_elements_stmtContext) {
1638                 Integer max = parseMaxElements((Max_elements_stmtContext) refineArg, refine.getModuleName());
1639                 refine.setMaxElements(max);
1640             } else if (refineArg instanceof Min_elements_stmtContext) {
1641                 Integer min = parseMinElements((Min_elements_stmtContext) refineArg, refine.getModuleName());
1642                 refine.setMinElements(min);
1643             }
1644         }
1645         return refine;
1646     }
1647
1648     private static RefineBuilder parseRefine(final RefineHolderImpl refine, final Refine_leaf_list_stmtsContext refineStmt) {
1649         for (int i = 0; i < refineStmt.getChildCount(); i++) {
1650             ParseTree refineArg = refineStmt.getChild(i);
1651             if (refineArg instanceof Must_stmtContext) {
1652                 MustDefinition must = parseMust((Must_stmtContext) refineArg);
1653                 refine.setMust(must);
1654             } else if (refineArg instanceof Max_elements_stmtContext) {
1655                 Integer max = parseMaxElements((Max_elements_stmtContext) refineArg, refine.getModuleName());
1656                 refine.setMaxElements(max);
1657             } else if (refineArg instanceof Min_elements_stmtContext) {
1658                 Integer min = parseMinElements((Min_elements_stmtContext) refineArg, refine.getModuleName());
1659                 refine.setMinElements(min);
1660             }
1661         }
1662         return refine;
1663     }
1664
1665     private static RefineBuilder parseRefine(final RefineHolderImpl refine, final Refine_choice_stmtsContext refineStmt) {
1666         for (int i = 0; i < refineStmt.getChildCount(); i++) {
1667             ParseTree refineArg = refineStmt.getChild(i);
1668             if (refineArg instanceof Default_stmtContext) {
1669                 String defaultStr = stringFromNode(refineArg);
1670                 refine.setDefaultStr(defaultStr);
1671             } else if (refineArg instanceof Mandatory_stmtContext) {
1672                 for (int j = 0; j < refineArg.getChildCount(); j++) {
1673                     ParseTree mandatoryTree = refineArg.getChild(j);
1674                     if (mandatoryTree instanceof Mandatory_argContext) {
1675                         Boolean mandatory = Boolean.valueOf(stringFromNode(mandatoryTree));
1676                         refine.setMandatory(mandatory);
1677                     }
1678                 }
1679             }
1680         }
1681         return refine;
1682     }
1683
1684     private static RefineBuilder parseRefine(final RefineBuilder refine, final Refine_anyxml_stmtsContext refineStmt) {
1685         for (int i = 0; i < refineStmt.getChildCount(); i++) {
1686             ParseTree refineArg = refineStmt.getChild(i);
1687             if (refineArg instanceof Must_stmtContext) {
1688                 MustDefinition must = parseMust((Must_stmtContext) refineArg);
1689                 refine.setMust(must);
1690             } else if (refineArg instanceof Mandatory_stmtContext) {
1691                 for (int j = 0; j < refineArg.getChildCount(); j++) {
1692                     ParseTree mandatoryTree = refineArg.getChild(j);
1693                     if (mandatoryTree instanceof Mandatory_argContext) {
1694                         Boolean mandatory = Boolean.valueOf(stringFromNode(mandatoryTree));
1695                         refine.setMandatory(mandatory);
1696                     }
1697                 }
1698             }
1699         }
1700         return refine;
1701     }
1702
1703     public static String getArgumentString(final org.antlr.v4.runtime.ParserRuleContext ctx) {
1704         List<StringContext> potentialValues = ctx.getRuleContexts(StringContext.class);
1705         checkState(!potentialValues.isEmpty());
1706         return ParserListenerUtils.stringFromStringContext(potentialValues.get(0));
1707     }
1708
1709     public static <T extends ParserRuleContext> Optional<T> getFirstContext(final ParserRuleContext context,final Class<T> contextType) {
1710         List<T> potential = context.getRuleContexts(contextType);
1711         if(potential.isEmpty()) {
1712             return Optional.absent();
1713         }
1714         return Optional.of(potential.get(0));
1715     }
1716
1717 }