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