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