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