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