Merge "Bug 1586: Fixed missing processing of single qouted strings."
[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 namespace
1095      *            current namespace
1096      * @param revision
1097      *            current revision
1098      * @param prefix
1099      *            current prefix
1100      * @param parent
1101      *            parent builder
1102      * @return TypeDefinition object based on parsed values.
1103      */
1104     public static TypeDefinition<?> parseTypeWithBody(final String typeName, final Type_body_stmtsContext typeBody,
1105             final SchemaPath actualPath, final QName moduleQName, final Builder parent) {
1106
1107         final String moduleName = parent.getModuleName();
1108         final int line = typeBody.getStart().getLine();
1109         TypeDefinition<?> baseType = null;
1110
1111         Integer fractionDigits = getFractionDigits(typeBody, moduleName);
1112         List<LengthConstraint> lengthStatements = getLengthConstraints(typeBody, moduleName);
1113         List<PatternConstraint> patternStatements = getPatternConstraint(typeBody);
1114         List<RangeConstraint> rangeStatements = getRangeConstraints(typeBody, moduleName);
1115
1116         TypeConstraints constraints = new TypeConstraints(moduleName, line);
1117         constraints.addFractionDigits(fractionDigits);
1118         constraints.addLengths(lengthStatements);
1119         constraints.addPatterns(patternStatements);
1120         constraints.addRanges(rangeStatements);
1121
1122         SchemaPath baseTypePath = createBaseTypePath(actualPath, typeName);
1123         SchemaPath extBaseTypePath = createExtendedBaseTypePath(actualPath, moduleQName, typeName);
1124
1125         if (parent instanceof TypeDefinitionBuilder && !(parent instanceof UnionTypeBuilder)) {
1126             extBaseTypePath = baseTypePath;
1127         }
1128
1129         if ("decimal64".equals(typeName)) {
1130             if (rangeStatements.isEmpty()) {
1131                 try {
1132                     return Decimal64.create(baseTypePath, fractionDigits);
1133                 } catch(Exception e) {
1134                     throw new YangParseException(moduleName, line, e.getMessage());
1135                 }
1136             }
1137             Decimal64 decimalType = Decimal64.create(extBaseTypePath, fractionDigits);
1138             constraints.addRanges(decimalType.getRangeConstraints());
1139             baseType = decimalType;
1140         } else if (typeName.startsWith("int")) {
1141             IntegerTypeDefinition intType = null;
1142             switch (typeName) {
1143             case "int8":
1144                 intType = Int8.getInstance();
1145                 break;
1146             case "int16":
1147                 intType = Int16.getInstance();
1148                 break;
1149             case "int32":
1150                 intType = Int32.getInstance();
1151                 break;
1152             case "int64":
1153                 intType = Int64.getInstance();
1154                 break;
1155             }
1156             if (intType == null) {
1157                 throw new YangParseException(moduleName, line, "Unknown yang type " + typeName);
1158             }
1159             constraints.addRanges(intType.getRangeConstraints());
1160             baseType = intType;
1161         } else if (typeName.startsWith("uint")) {
1162             UnsignedIntegerTypeDefinition uintType = null;
1163             switch (typeName) {
1164             case "uint8":
1165                 uintType = Uint8.getInstance();
1166                 break;
1167             case "uint16":
1168                 uintType = Uint16.getInstance();
1169                 break;
1170             case "uint32":
1171                 uintType = Uint32.getInstance();
1172                 break;
1173             case "uint64":
1174                 uintType = Uint64.getInstance();
1175                 break;
1176             }
1177             if (uintType == null) {
1178                 throw new YangParseException(moduleName, line, "Unknown yang type " + typeName);
1179             }
1180             constraints.addRanges(uintType.getRangeConstraints());
1181             baseType = uintType;
1182         } else if ("enumeration".equals(typeName)) {
1183             List<EnumTypeDefinition.EnumPair> enumConstants = getEnumConstants(typeBody, actualPath, moduleName);
1184             return EnumerationType.create(baseTypePath, enumConstants, Optional.<EnumPair> absent());
1185         } else if ("string".equals(typeName)) {
1186             StringTypeDefinition stringType = StringType.getInstance();
1187             constraints.addLengths(stringType.getLengthConstraints());
1188             baseType = stringType;
1189         } else if ("bits".equals(typeName)) {
1190             return BitsType.create(baseTypePath, getBits(typeBody, actualPath, moduleName));
1191         } else if ("leafref".equals(typeName)) {
1192             final String path = parseLeafrefPath(typeBody);
1193             final boolean absolute = path.startsWith("/");
1194             RevisionAwareXPath xpath = new RevisionAwareXPathImpl(path, absolute);
1195             return new Leafref(xpath);
1196         } else if ("binary".equals(typeName)) {
1197             BinaryTypeDefinition binaryType = BinaryType.getInstance();
1198             constraints.addLengths(binaryType.getLengthConstraints());
1199             baseType = binaryType;
1200         } else if ("instance-identifier".equals(typeName)) {
1201             return InstanceIdentifierType.create(isRequireInstance(typeBody));
1202         }
1203
1204         if (parent instanceof TypeDefinitionBuilder && !(parent instanceof UnionTypeBuilder)) {
1205             TypeDefinitionBuilder typedef = (TypeDefinitionBuilder) parent;
1206             typedef.setRanges(constraints.getRange());
1207             typedef.setLengths(constraints.getLength());
1208             typedef.setPatterns(constraints.getPatterns());
1209             typedef.setFractionDigits(constraints.getFractionDigits());
1210             return baseType;
1211         }
1212
1213         QName qname = QName.create(moduleQName, typeName);
1214         SchemaPath schemaPath = actualPath.createChild(qname);
1215         final Optional<String> opt = Optional.of("");
1216         ExtendedType.Builder typeBuilder = ExtendedType.builder(qname, baseType, opt, opt, schemaPath);
1217
1218         typeBuilder.ranges(constraints.getRange());
1219         typeBuilder.lengths(constraints.getLength());
1220         typeBuilder.patterns(constraints.getPatterns());
1221         typeBuilder.fractionDigits(constraints.getFractionDigits());
1222
1223         return typeBuilder.build();
1224     }
1225
1226     private static SchemaPath createTypePath(final SchemaPath actual, final String typeName) {
1227         QName last = actual.getLastComponent();
1228         return actual.createChild(QName.create(last, typeName));
1229     }
1230
1231     private static SchemaPath createBaseTypePath(final SchemaPath actual, final String typeName) {
1232         return actual.createChild(BaseTypes.constructQName(typeName));
1233     }
1234
1235     private static SchemaPath createExtendedBaseTypePath(final SchemaPath actual, final QName moduleQName, final String typeName) {
1236         return actual.createChild(
1237                 QName.create(moduleQName, typeName),
1238                 BaseTypes.constructQName(typeName));
1239     }
1240
1241     /**
1242      * Parse given context and find identityref base value.
1243      *
1244      * @param ctx
1245      *            type body
1246      * @return identityref base value as String
1247      */
1248     public static String getIdentityrefBase(final Type_body_stmtsContext ctx) {
1249         String result = null;
1250         outer: for (int i = 0; i < ctx.getChildCount(); i++) {
1251             ParseTree child = ctx.getChild(i);
1252             if (child instanceof Identityref_specificationContext) {
1253                 for (int j = 0; j < child.getChildCount(); j++) {
1254                     ParseTree baseArg = child.getChild(j);
1255                     if (baseArg instanceof Base_stmtContext) {
1256                         result = stringFromNode(baseArg);
1257                         break outer;
1258                     }
1259                 }
1260             }
1261         }
1262         return result;
1263     }
1264
1265     /**
1266      * Parse type body statement and find require-instance value.
1267      *
1268      * @param ctx
1269      *            type body context
1270      * @return require-instance value
1271      */
1272     private static boolean isRequireInstance(final Type_body_stmtsContext ctx) {
1273         for (int i = 0; i < ctx.getChildCount(); i++) {
1274             ParseTree child = ctx.getChild(i);
1275             if (child instanceof Instance_identifier_specificationContext) {
1276                 for (int j = 0; j < child.getChildCount(); j++) {
1277                     ParseTree reqStmt = child.getChild(j);
1278                     if (reqStmt instanceof Require_instance_stmtContext) {
1279                         for (int k = 0; k < reqStmt.getChildCount(); k++) {
1280                             ParseTree reqArg = reqStmt.getChild(k);
1281                             if (reqArg instanceof Require_instance_argContext) {
1282                                 return Boolean.valueOf(stringFromNode(reqArg));
1283                             }
1284                         }
1285                     }
1286                 }
1287             }
1288         }
1289         return true;
1290     }
1291
1292     /**
1293      * Parse type body statement and find leafref path.
1294      *
1295      * @param ctx
1296      *            type body context
1297      * @return leafref path as String
1298      */
1299     private static String parseLeafrefPath(final Type_body_stmtsContext ctx) {
1300         for (int i = 0; i < ctx.getChildCount(); i++) {
1301             ParseTree child = ctx.getChild(i);
1302             if (child instanceof Leafref_specificationContext) {
1303                 for (int j = 0; j < child.getChildCount(); j++) {
1304                     ParseTree leafRefSpec = child.getChild(j);
1305                     if (leafRefSpec instanceof Path_stmtContext) {
1306                         return stringFromNode(leafRefSpec);
1307                     }
1308                 }
1309             }
1310         }
1311         return null;
1312     }
1313
1314     /**
1315      * Internal helper method for parsing must statement.
1316      *
1317      * @param ctx
1318      *            Must_stmtContext
1319      * @return MustDefinition object based on parsed context
1320      */
1321     private static MustDefinition parseMust(final YangParser.Must_stmtContext ctx) {
1322         StringBuilder mustText = new StringBuilder();
1323         Optional<String> description = Optional.absent();
1324         Optional<String> reference = Optional.absent();
1325         Optional<String> errorAppTag = Optional.absent();
1326         Optional<String> errorMessage = Optional.absent();
1327         for (int i = 0; i < ctx.getChildCount(); ++i) {
1328             ParseTree child = ctx.getChild(i);
1329             if (child instanceof StringContext) {
1330                 final StringContext context = (StringContext) child;
1331                 if (context.getChildCount() == 1) {
1332                     String mustPart = context.getChild(0).getText();
1333                     // trim start and end quotation
1334                     mustText.append(mustPart.substring(1, mustPart.length() - 1));
1335                 } else {
1336                     for (int j = 0; j < context.getChildCount(); j++) {
1337                         String mustPart = context.getChild(j).getText();
1338                         if (j == 0) {
1339                             mustText.append(mustPart.substring(0, mustPart.length() - 1));
1340                             continue;
1341                         }
1342                         if (j % 2 == 0) {
1343                             mustText.append(mustPart.substring(1));
1344                         }
1345                     }
1346                 }
1347             } else if (child instanceof Description_stmtContext) {
1348                 description = Optional.of(stringFromNode(child));
1349             } else if (child instanceof Reference_stmtContext) {
1350                 reference = Optional.of(stringFromNode(child));
1351             } else if (child instanceof Error_app_tag_stmtContext) {
1352                 errorAppTag = Optional.of(stringFromNode(child));
1353             } else if (child instanceof Error_message_stmtContext) {
1354                 errorMessage = Optional.of(stringFromNode(child));
1355             }
1356         }
1357
1358         return MustDefinitionImpl.create(mustText.toString(), description, reference, errorAppTag, errorMessage);
1359     }
1360
1361     /**
1362      * Parse given context and set constraints to constraints builder.
1363      *
1364      * @param ctx
1365      *            context to parse
1366      * @param constraints
1367      *            ConstraintsBuilder to fill
1368      */
1369     public static void parseConstraints(final ParseTree ctx, final ConstraintsBuilder constraints) {
1370         for (int i = 0; i < ctx.getChildCount(); ++i) {
1371             final ParseTree childNode = ctx.getChild(i);
1372             if (childNode instanceof Max_elements_stmtContext) {
1373                 Integer max = parseMaxElements((Max_elements_stmtContext) childNode, constraints.getModuleName());
1374                 constraints.setMaxElements(max);
1375             } else if (childNode instanceof Min_elements_stmtContext) {
1376                 Integer min = parseMinElements((Min_elements_stmtContext) childNode, constraints.getModuleName());
1377                 constraints.setMinElements(min);
1378             } else if (childNode instanceof Must_stmtContext) {
1379                 MustDefinition must = parseMust((Must_stmtContext) childNode);
1380                 constraints.addMustDefinition(must);
1381             } else if (childNode instanceof Mandatory_stmtContext) {
1382                 for (int j = 0; j < childNode.getChildCount(); j++) {
1383                     ParseTree mandatoryTree = childNode.getChild(j);
1384                     if (mandatoryTree instanceof Mandatory_argContext) {
1385                         Boolean mandatory = Boolean.valueOf(stringFromNode(mandatoryTree));
1386                         constraints.setMandatory(mandatory);
1387                     }
1388                 }
1389             } else if (childNode instanceof When_stmtContext) {
1390                 constraints.addWhenCondition(stringFromNode(childNode));
1391             }
1392         }
1393     }
1394
1395     private static Integer parseMinElements(final Min_elements_stmtContext ctx, final String moduleName) {
1396         Integer result = null;
1397         try {
1398             for (int i = 0; i < ctx.getChildCount(); i++) {
1399                 ParseTree minArg = ctx.getChild(i);
1400                 if (minArg instanceof Min_value_argContext) {
1401                     result = Integer.valueOf(stringFromNode(minArg));
1402                 }
1403             }
1404             if (result == null) {
1405                 throw new IllegalArgumentException();
1406             }
1407             return result;
1408         } catch (Exception e) {
1409             throw new YangParseException(moduleName, ctx.getStart().getLine(), "Failed to parse min-elements.", e);
1410         }
1411     }
1412
1413     private static Integer parseMaxElements(final Max_elements_stmtContext ctx, final String moduleName) {
1414         Integer result = null;
1415         try {
1416             for (int i = 0; i < ctx.getChildCount(); i++) {
1417                 ParseTree maxArg = ctx.getChild(i);
1418                 if (maxArg instanceof Max_value_argContext) {
1419                     String maxValue = stringFromNode(maxArg);
1420                     if ("unbounded".equals(maxValue)) {
1421                         result = Integer.MAX_VALUE;
1422                     } else {
1423                         result = Integer.valueOf(maxValue);
1424                     }
1425                 }
1426             }
1427             if (result == null) {
1428                 throw new IllegalArgumentException();
1429             }
1430             return result;
1431         } catch (Exception e) {
1432             throw new YangParseException(moduleName, ctx.getStart().getLine(), "Failed to parse max-elements.", e);
1433         }
1434     }
1435
1436     /**
1437      * Parse given context and return yin value.
1438      *
1439      * @param ctx
1440      *            context to parse
1441      * @return true if value is 'true', false otherwise
1442      */
1443     public static boolean parseYinValue(final Argument_stmtContext ctx) {
1444         boolean yinValue = false;
1445         outer: for (int i = 0; i < ctx.getChildCount(); i++) {
1446             ParseTree yin = ctx.getChild(i);
1447             if (yin instanceof Yin_element_stmtContext) {
1448                 for (int j = 0; j < yin.getChildCount(); j++) {
1449                     ParseTree yinArg = yin.getChild(j);
1450                     if (yinArg instanceof Yin_element_argContext) {
1451                         String yinString = stringFromNode(yinArg);
1452                         if ("true".equals(yinString)) {
1453                             yinValue = true;
1454                             break outer;
1455                         }
1456                     }
1457                 }
1458             }
1459         }
1460         return yinValue;
1461     }
1462
1463     /**
1464      * Check this base type.
1465      *
1466      * @param typeName
1467      *            base YANG type name
1468      * @param moduleName
1469      *            name of current module
1470      * @param line
1471      *            line in module
1472      * @throws YangParseException
1473      *             if this is one of YANG type which MUST contain additional
1474      *             informations in its body
1475      */
1476     public static void checkMissingBody(final String typeName, final String moduleName, final int line) {
1477         switch (typeName) {
1478         case "decimal64":
1479             throw new YangParseException(moduleName, line,
1480                     "The 'fraction-digits' statement MUST be present if the type is 'decimal64'.");
1481         case "identityref":
1482             throw new YangParseException(moduleName, line,
1483                     "The 'base' statement MUST be present if the type is 'identityref'.");
1484         case "leafref":
1485             throw new YangParseException(moduleName, line,
1486                     "The 'path' statement MUST be present if the type is 'leafref'.");
1487         case "bits":
1488             throw new YangParseException(moduleName, line, "The 'bit' statement MUST be present if the type is 'bits'.");
1489         case "enumeration":
1490             throw new YangParseException(moduleName, line,
1491                     "The 'enum' statement MUST be present if the type is 'enumeration'.");
1492         }
1493     }
1494
1495     /**
1496      * Parse refine statement.
1497      *
1498      * @param refineCtx
1499      *            refine statement
1500      * @param moduleName
1501      *            name of current module
1502      * @return RefineHolder object representing this refine statement
1503      */
1504     public static RefineHolderImpl parseRefine(final Refine_stmtContext refineCtx, final String moduleName) {
1505         final String refineTarget = stringFromNode(refineCtx);
1506         final RefineHolderImpl refine = new RefineHolderImpl(moduleName, refineCtx.getStart().getLine(), refineTarget);
1507         for (int i = 0; i < refineCtx.getChildCount(); i++) {
1508             ParseTree refinePom = refineCtx.getChild(i);
1509             if (refinePom instanceof Refine_pomContext) {
1510                 for (int j = 0; j < refinePom.getChildCount(); j++) {
1511                     ParseTree refineStmt = refinePom.getChild(j);
1512                     parseRefineDefault(refine, refineStmt);
1513
1514                     if (refineStmt instanceof Refine_leaf_stmtsContext) {
1515                         parseRefine(refine, (Refine_leaf_stmtsContext) refineStmt);
1516                     } else if (refineStmt instanceof Refine_container_stmtsContext) {
1517                         parseRefine(refine, (Refine_container_stmtsContext) refineStmt);
1518                     } else if (refineStmt instanceof Refine_list_stmtsContext) {
1519                         parseRefine(refine, (Refine_list_stmtsContext) refineStmt);
1520                     } else if (refineStmt instanceof Refine_leaf_list_stmtsContext) {
1521                         parseRefine(refine, (Refine_leaf_list_stmtsContext) refineStmt);
1522                     } else if (refineStmt instanceof Refine_choice_stmtsContext) {
1523                         parseRefine(refine, (Refine_choice_stmtsContext) refineStmt);
1524                     } else if (refineStmt instanceof Refine_anyxml_stmtsContext) {
1525                         parseRefine(refine, (Refine_anyxml_stmtsContext) refineStmt);
1526                     }
1527                 }
1528             }
1529         }
1530         return refine;
1531     }
1532
1533     private static void parseRefineDefault(final RefineHolderImpl refine, final ParseTree refineStmt) {
1534         for (int i = 0; i < refineStmt.getChildCount(); i++) {
1535             ParseTree refineArg = refineStmt.getChild(i);
1536             if (refineArg instanceof Description_stmtContext) {
1537                 String description = stringFromNode(refineArg);
1538                 refine.setDescription(description);
1539             } else if (refineArg instanceof Reference_stmtContext) {
1540                 String reference = stringFromNode(refineArg);
1541                 refine.setReference(reference);
1542             } else if (refineArg instanceof Config_stmtContext) {
1543                 Boolean config = parseConfig((Config_stmtContext) refineArg, refine.getModuleName());
1544                 refine.setConfiguration(config);
1545             }
1546         }
1547     }
1548
1549     private static RefineBuilder parseRefine(final RefineHolderImpl refine, final Refine_leaf_stmtsContext refineStmt) {
1550         for (int i = 0; i < refineStmt.getChildCount(); i++) {
1551             ParseTree refineArg = refineStmt.getChild(i);
1552             if (refineArg instanceof Default_stmtContext) {
1553                 String defaultStr = stringFromNode(refineArg);
1554                 refine.setDefaultStr(defaultStr);
1555             } else if (refineArg instanceof Mandatory_stmtContext) {
1556                 for (int j = 0; j < refineArg.getChildCount(); j++) {
1557                     ParseTree mandatoryTree = refineArg.getChild(j);
1558                     if (mandatoryTree instanceof Mandatory_argContext) {
1559                         Boolean mandatory = Boolean.valueOf(stringFromNode(mandatoryTree));
1560                         refine.setMandatory(mandatory);
1561                     }
1562                 }
1563             } else if (refineArg instanceof Must_stmtContext) {
1564                 MustDefinition must = parseMust((Must_stmtContext) refineArg);
1565                 refine.setMust(must);
1566
1567             }
1568         }
1569         return refine;
1570     }
1571
1572     private static RefineBuilder parseRefine(final RefineBuilder refine, final Refine_container_stmtsContext refineStmt) {
1573         for (int i = 0; i < refineStmt.getChildCount(); i++) {
1574             ParseTree refineArg = refineStmt.getChild(i);
1575             if (refineArg instanceof Must_stmtContext) {
1576                 MustDefinition must = parseMust((Must_stmtContext) refineArg);
1577                 refine.setMust(must);
1578             } else if (refineArg instanceof Presence_stmtContext) {
1579                 refine.setPresence(true);
1580             }
1581         }
1582         return refine;
1583     }
1584
1585     private static RefineBuilder parseRefine(final RefineHolderImpl refine, final Refine_list_stmtsContext refineStmt) {
1586         for (int i = 0; i < refineStmt.getChildCount(); i++) {
1587             ParseTree refineArg = refineStmt.getChild(i);
1588             if (refineArg instanceof Must_stmtContext) {
1589                 MustDefinition must = parseMust((Must_stmtContext) refineArg);
1590                 refine.setMust(must);
1591             } else if (refineArg instanceof Max_elements_stmtContext) {
1592                 Integer max = parseMaxElements((Max_elements_stmtContext) refineArg, refine.getModuleName());
1593                 refine.setMaxElements(max);
1594             } else if (refineArg instanceof Min_elements_stmtContext) {
1595                 Integer min = parseMinElements((Min_elements_stmtContext) refineArg, refine.getModuleName());
1596                 refine.setMinElements(min);
1597             }
1598         }
1599         return refine;
1600     }
1601
1602     private static RefineBuilder parseRefine(final RefineHolderImpl refine, final Refine_leaf_list_stmtsContext refineStmt) {
1603         for (int i = 0; i < refineStmt.getChildCount(); i++) {
1604             ParseTree refineArg = refineStmt.getChild(i);
1605             if (refineArg instanceof Must_stmtContext) {
1606                 MustDefinition must = parseMust((Must_stmtContext) refineArg);
1607                 refine.setMust(must);
1608             } else if (refineArg instanceof Max_elements_stmtContext) {
1609                 Integer max = parseMaxElements((Max_elements_stmtContext) refineArg, refine.getModuleName());
1610                 refine.setMaxElements(max);
1611             } else if (refineArg instanceof Min_elements_stmtContext) {
1612                 Integer min = parseMinElements((Min_elements_stmtContext) refineArg, refine.getModuleName());
1613                 refine.setMinElements(min);
1614             }
1615         }
1616         return refine;
1617     }
1618
1619     private static RefineBuilder parseRefine(final RefineHolderImpl refine, final Refine_choice_stmtsContext refineStmt) {
1620         for (int i = 0; i < refineStmt.getChildCount(); i++) {
1621             ParseTree refineArg = refineStmt.getChild(i);
1622             if (refineArg instanceof Default_stmtContext) {
1623                 String defaultStr = stringFromNode(refineArg);
1624                 refine.setDefaultStr(defaultStr);
1625             } else if (refineArg instanceof Mandatory_stmtContext) {
1626                 for (int j = 0; j < refineArg.getChildCount(); j++) {
1627                     ParseTree mandatoryTree = refineArg.getChild(j);
1628                     if (mandatoryTree instanceof Mandatory_argContext) {
1629                         Boolean mandatory = Boolean.valueOf(stringFromNode(mandatoryTree));
1630                         refine.setMandatory(mandatory);
1631                     }
1632                 }
1633             }
1634         }
1635         return refine;
1636     }
1637
1638     private static RefineBuilder parseRefine(final RefineBuilder refine, final Refine_anyxml_stmtsContext refineStmt) {
1639         for (int i = 0; i < refineStmt.getChildCount(); i++) {
1640             ParseTree refineArg = refineStmt.getChild(i);
1641             if (refineArg instanceof Must_stmtContext) {
1642                 MustDefinition must = parseMust((Must_stmtContext) refineArg);
1643                 refine.setMust(must);
1644             } else if (refineArg instanceof Mandatory_stmtContext) {
1645                 for (int j = 0; j < refineArg.getChildCount(); j++) {
1646                     ParseTree mandatoryTree = refineArg.getChild(j);
1647                     if (mandatoryTree instanceof Mandatory_argContext) {
1648                         Boolean mandatory = Boolean.valueOf(stringFromNode(mandatoryTree));
1649                         refine.setMandatory(mandatory);
1650                     }
1651                 }
1652             }
1653         }
1654         return refine;
1655     }
1656
1657     public static String getArgumentString(final org.antlr.v4.runtime.ParserRuleContext ctx) {
1658         List<StringContext> potentialValues = ctx.getRuleContexts(StringContext.class);
1659         checkState(!potentialValues.isEmpty());
1660         return ParserListenerUtils.stringFromStringContext(potentialValues.get(0));
1661     }
1662
1663     public static <T extends ParserRuleContext> Optional<T> getFirstContext(final ParserRuleContext context,final Class<T> contextType) {
1664         List<T> potential = context.getRuleContexts(contextType);
1665         if(potential.isEmpty()) {
1666             return Optional.absent();
1667         }
1668         return Optional.of(potential.get(0));
1669     }
1670
1671 }