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