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