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